Promise整理

2018-02-21  本文已影响0人  盛夏晚清风

在开发过程中,我们经常需要处理异步的情况,开发人员对异步的处理方式也从轮询到回调,再到现在的Promise。于是,我打算对Promise整理一番。

Promise类似于XMLHttpRequest,通过构造函数Promise来创建一个新Promise对象作为接口。要想创建一个promise对象,可以使用new来调用Promise的构造器实例化:

var promise=new Promise(function(resolve,reject){
    //异步处理
    //处理结束后,调用resolve或reject
})

Promise的状态

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

Promise.resolve

Promise.resolve(value)可以认为是以下代码的语法糖:

new Promise(function(resolve){
    resolve(value); 
});

在这段代码中的resolve(value);会让这个promise对象立即进入确定(即resolved)状态,并将value传递给后面then里所指定的onFulfilled函数。

方法Promise.resolve(value);的返回值也是一个promise对象,所以我们可以像下面那样接着对其返回值进行.then调用:

Promise.resolve(value).then(function(value){
    console.log(value);
})

Promise.resolve方法的另一个作用就是将thenable对象转换为promise对象。

thenable,简单来说是一个非常类似于promise的东西,它指一个具有.then方法的对象。例如jQuery.ajax(),它的返回值是一个具有.then方法的jqXHR Object对象,这个对象继承了来自Deferred Object的方法和属性。但是Deferred Object并没有遵循Promises/A+或ES6 Promises标准,所以即使看上去这个对象转换成了一个promise对象,但是二者的then方法机制不同。

将thenable对象转换成promise对象:

var promise=Promise.resolve($.ajax(...));
promise.then(function(value){
    console.log(value);
});

简单总结一下Promise.resolve方法的话,可以认为它的作用就是将传递给它的参数填充(Fulfilled)到promise对象后并返回这个promise对象,它的参数主要分为以下三类:

  1. 接收到promise对象参数时:返回该promise对象
  2. 接收到thenable类型的对象时:返回一个新的promise对象,该对象具有一个then方法;
  3. 接收到其他类型的参数(或者参数为空)时:返回一个将该参数作为值的新promise对象

另外,调用resolve或reject并不会终结Promise的参数函数的执行:

var promise = new Promise(function (resolve){
    console.log("promise1"); 
    resolve(42);
    console.log("promise2"); 
});
promise.then(function(value){
    console.log(value);
});
结果:

上面代码中,虽然在调用了 resolve(42)之后打印promise2,但是实际上promise2先打印出来。

Promise.reject

Promise.reject(error)可以认为是以下代码的语法糖:

new Promise(function(resolve,reject){
    reject(new Error("出错了")); 
});

Promise只能进行异步操作?

promise是同步的,.then是异步的(规定:Promise只能使用异步调用方式):

var promise = new Promise(function (resolve){
    console.log("inner promise"); 
    resolve(42);
});
promise.then(function(value){
    console.log(value);
});
console.log("outer promise"); 
结果:

依照结果来看,打印出"inner promise"后紧接着打印出了"outer promise",最后才调用.then方法打印出value。所以为了避免同步调用和异步调用同时存在导致的混乱,即使.then方法可以立即调用(即同步调用),Promise也会以异步的方式调用该回调函数,这是在Promise设计上的规定方针

所以这个方针也告诉我们,如果我们的回调函数可能会同步调用,也可能异步调用,最好选择同一使用异步调用的方式。例如,将代码:

function onReady(fn) {
    var readyState = document.readyState;
    if (readyState === 'interactive' || readyState === 'complete') {
        fn();    // 注意这里
    } else {
        window.addEventListener('DOMContentLoaded', fn);
    }
}
onReady(function () {
    console.log('DOM fully loaded and parsed');
});
console.log('==Starting==');

中的fn改为:

setTimeout(fn, 0);

这样,同步的fn回调就变为了异步的fn回调。

Promise.all

Promise.all接收一个promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态时,它才会去调用.then方法,并且.then方法中的的执行结果的参数顺序与all()中各promise对象的顺序一致。

举例:

var promise1=ajax({...});
var promise2=ajax({...});
Promise.all([promise1,promise2]).then(fn);

只有promise1和promise2都完成了,才会去调用then方法。

注意点:传递给Promise.all的promise并不是一个个顺序执行的,而是同时开始、并行执行的。对于上面的例子,promise1和promise2同时开始、并行执行。

Promise.race

Promise.race接收一个promise对象的数组作为参数,当这个数组里的所有promise对象中只要有一项变为resolve或reject状态时,它就会去调用.then方法,并且.then方法中的的执行结果的参数即为该promise对象的回调参数。

举例:

var promise1=ajax({...});
var promise2=ajax({...});
Promise.race([promise1,promise2]).then(fn);

只要promise1和promise2其中有一个完成,就会去调用then方法。

注意点:当Promise.race中的第一个promise对象变为确定(fulfilled)状态后,它之后的promise对象会继续运行,不会取消或者中断。对于上面的例子,只要promise1或者promise2其中某一个变为确定态,就会去调用then方法。

其他注意点

  1. .catch是语法糖,promise.catch(function(error){...})等价于promise.then(undefined,function(error){...})

  2. 每次调用then都会返回一个新创建的promise对象

  3. 使用promise.then(onFulfilled, onRejected)时,如果onFulfilled中发生异常,在onRejected中是捕获不到这个异常的;而在promise.then(onFulfilled).catch(onRejected)中,.then中的异常能在.catch中捕获。

  4. 使用reject而非throw

由于个人水平有限,博客错误之处,烦请指正!

参考资料:
1、JavaScript Promise迷你书
2、Promise/A+规范

上一篇 下一篇

猜你喜欢

热点阅读