转载

Promise--优雅解决回调嵌套(一)

用 new Promise 实例化的Promise对象有三个状态:

  • “has-resolution” - Fulfilled

    • reslove(成功时),调用 onFulfilled
  • “has-rejection” - Rejected

    • reject(失败时)。调用 Rejected
  • “unresolve” - Pending

    • 既不是resolve也不是reject状态,也就是Promise刚刚被创建后的初始化状态。

Promise--优雅解决回调嵌套(一)

note:

  1. 在Chrome中输出 resolve 可以得到 Promise {[[PromiseStatus]]: "pending", [[PromiseValue]]: undefined} ,可以看出 [[PromiseStatus]] 中存储的就是Promise的状态,但是并没有公开访问 [[PromiseStatus]] 的用户API,所以暂时还无法查询其内部状态。

  2. Promise中的 then 的回调只会被调用一次,因为Promise的状态只会从Pending变为Fulfilled或者Rejected,不可逆。

Promise的使用

在使用Promise实现有序执行异步的基本格式如下:

//defined Promise async function
functionasyncFun(){
    return new Promise((reslove,reject)=>{
        if(reslove){
            reslove(/*reslove parameter*/);
        }else{
            reject(new Error(/*Error*/));
        }
    })
}

//use Promise&then
asyncFun().then(/*function*/).then(/*function*/)...

reslove 方法的参数就是要传给回调函数的参数,即 resolve 将运行得到的结果传出来,而 then 接受该参数给回调继续执行后面的,如果这个 then 的中的函数还会返回Promise,则会重复执行该步骤直到结束。

reject 方法的参数一般是包含了reject原因的Error对象。 reject 和 resolve 一样,也会将自己的参数传出去,接收该参数的是 then 的第二个fun或者是 catch 。其实 .catch 只是 Promise.then(onFulfilled,onRejected) 的别名而已。

快捷创建Promise

一般情况下我们会使用 new Promise 来创建prmise对象,除此之外我们也可以使用 Promise.reslove 和 Promise.reject 来直接创建,例如 Promise.resolve(42) 可以认为是以下代码的语法糖

new Promise((reslove)=>{
    reslove(42);
});

这段代码可以让这个Promise对象立即进入resolve状态,并将42传递给后面then里所指定的 onFulfilled 函数。此外 Promise.resolve 还有一个作用,就是将非Promise对象转换为Promise对象。

Promise.reject(value) 与之类似。

Promise.then()的异步调用带来的思考

var promise = new Promise(function(resolve){
    console.log("inner promise"); // 1
    resolve(42);
});
promise.then(function(value){
    console.log(value); // 3
});
console.log("outer promise"); // 2

/*输出:
"inner promise"
"outer promise"
42
*/

从以上的这段代码我们可以看出 Promise.then() 是异步调用的,这也是Promise设计上规定的,其原因在于 同步调用和异步调用同时存在会导致混乱 。

以上那段代码如果在 调用onReady之前DOM已经载入的话 ,对回调函数进行 同步 调用,如果在 调用onReady之前DOM还没有载入的话 ,通过注册 DOMContentLoader 事件监听器来对回调进行 异步 调用。这会导致该代码在源文件中不同位置输出不同的结果,关于这个现象,有如下几点:

  • 绝对不能对异步函数(即使在数据已经就绪)进行同步调用
  • 如果对异步回调函数进行同步调用,处理顺序可能会与预期不符,带来意外的结果
  • 对异步回调函数进行同步调用,还可能导致栈溢出或者异常处理错乱等问题
  • 如果想在将来的某个时刻调用异步回调,可以使用 setTimeout 等异步API

所以以上代码应该使用 setTimeout(fn, 0) 进行调用。

functiononReady(fn){
    var readyState = document.readyState;
    if (readyState === 'interactive' || readyState === 'complete') {
        setTimeout(fn, 0);
    } else {
        window.addEventListener('DOMContentLoaded', fn);
    }
}
onReady(function(){
    console.log('DOM fully loaded and parsed');
});
console.log('==Starting==');

所以在Promise中 then 是异步的。

关于Promise链式调用

如果想实现Promise的链式调用,要求每次链式调用都返回Promise。而then()方法返回就会返回一个Promise,所以只需要给then传入两个回调即可,这两个回调分别在Promise的success和faliure情况下执行。所以现在问题的关键就是如何在第一次调用函数后就返回Promise,方法有两种:

  • 直接返回一个Promise
  • 利用我们前面提到过的 Promise.reslove() 语法糖。
//直接返回Promise
functionpro1(){
    return new Promise((reslove,reject)=>{
        if(reslove){
            reslove(setTimeout(()=>{console.log(1000)},1000));
        }
    })
}

functionpro2(){
    setTimeout(()=>{console.log(2000)},2000);
}

functionpro3(){
    setTimeout(()=>{console.log(3000)},3000);
}

pro1().then(pro2()).then(pro3());

//使用Promise.reslove()

functionpro1(){
    setTimeout(()=>{console.log(1000)},1000);
}
//这里的pro2,pro3与上面一样
Promise.resolve().then(pro1).then(pro2).then(pro3);

note:

  1. reslove 函数的作用是将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将 异步操作的结果作为参数传递出去 ;
  2. reject 函数的作用是将Promise对象状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时候调用,并将异步操作报出的错误作为参数传递出去;

而想要 then() 里的第一个参数执行,需要Promise是resolve状态,所以需要在启动Promise的时候,如果到达resolve,则给resolve传入要执行的函数,这样,传递。

原文  http://mrzhang123.github.io/2016/12/05/promise-1/
正文到此结束
Loading...