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

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

  • “has-resolution” - Fulfilled

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

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

    • 既不是resolve也不是reject状态,也就是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对象。 rejectresolve 一样,也会将自己的参数传出去,接收该参数的是 then 的第二个fun或者是 catch 。其实 .catch 只是 Promise.then(onFulfilled,onRejected) 的别名而已。

快捷创建Promise

一般情况下我们会使用 new Promise 来创建prmise对象,除此之外我们也可以使用 Promise.reslovePromise.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传入要执行的函数,这样,传递。

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章