关于ES6的Promise

1,297 阅读4分钟

JavaScript的异步处理

提到JavaScript的异步处理,也许很多人和我一样想到利用回调函数。

例如:

firstAsync(function(data){
    //处理得到的 data 数据
    //....
    secondAsync(function(data2){
        //处理得到的 data2 数据
        //....
        thirdAsync(function(data3){
              //处理得到的 data3 数据
              //....
        });
    });
});

但是回调函数一旦嵌套层级过多,就会引发“回调地狱”的问题,而Promise作为一种异步解决方案,就可以很好的解决这个问题。

例如上面的例子用Promise实现的话:

firstAsync()
.then(function(data){
    //处理得到的 data 数据
    //....
    return secondAsync();
})
.then(function(data2){
    //处理得到的 data2 数据
    //....
    return thirdAsync();
})
.then(function(data3){
    //处理得到的 data3 数据
    //....
});

什么是 Promise?

Promise是一个对象,用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。它作为ES6最重要的特性之一,目前已经被Firefox和Chrome等主流浏览器所支持,具体情况可以在Can I use里查看。如果想要做浏览器的兼容,只需要在浏览器中加载Polyfill类库即可。

Promise 的状态

  • 等待(pending):初始状态。
  • 已完成(fulfilled):意味着操作成功完成。
  • 已失败(rejected):意味着操作失败。

Promise对象的状态只可能处于这三种之一,它的状态改变只可能是两种可能:从 Pending 变为 fulfilled 和从 Pending 变为 Rejected。一旦状态发生改变,就不会再变,这也是Promise[承诺]这个名字的由来。

then 链式操作和 resolve 方法

Promise.prototype.then 方法返回的是一个新的 Promise 对象,因此可以采用链式写法。该方法接收两个参数,第一个是成功的回调,第二个是失败的回调。

下面用setTimeout模拟异步操作:

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('异步操作1执行完毕!');
            resolve('异步操作1成功回调数据');
        }, 1000);
    });
    return p;
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('异步操作2执行完毕!');
            resolve('异步操作2成功回调数据');
        }, 1000);
    });
    return p;
}

使用 Promise.prototype.then 方法链式调用这两个方法:

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
   console.log(data);
});

运行结果如下:

你也可以在 then 方法中直接return数据而不是Promise对象:

runAsync1()
.then(function(data){
    console.log(data);
    return runAsync2();
})
.then(function(data){
   console.log(data);
   return '直接返回数据'
})
.then(function(data){
    console.log(data)
});

运行结果如下:

reject 方法

上面我们通过 Promise.resolve() 把Promise的状态置为已完成(Resolved), 然后通过 then 方法捕捉到变化,并执行“成功”情况的回调,而 Promise.reject() 的作用就是将Promise的状态置为已失败(rejected),同样通过 then 方法来执行“失败”情况的回调。

function runAsync1(num){
    var p = new Promise(function(resolve, reject){  
        setTimeout(function(){
            console.log('异步操作1执行完毕!');
            if(num < 10) {
                resolve('异步操作1成功回调数据');
            }else{
                reject('异步操作1失败回调数据'); 
            }

        }, 1000);
    });
    return p;
}

运行结果如下:

catch 方法

Promise.prototype.catch 和 Promise.prototype.then 方法的第二个参数一样,用来指定reject的回调,二者都返回 promise 对象, 因此都可以被链式调用。

此外 catch 还可以捕获异常:

all 方法

Promise.all(iterable) 返回一个新的promise对象,该promise对象只有在iterable参数对象里所有的promise对象都成功的时候才会触发成功,并所有promise返回值以数组的形式返回。

以上面的 runAsync1() 和 runAsync1() 为例:

Promise
.all([runAsync1(),runAsync2()])
.then(function(results){
    console.log(results);
});

返回结果如下:

race 方法

Promise.race() 方法,race即竞赛,顾名思义,这是一个比谁跑得快的规则。

function runAsync1(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('异步操作1执行完毕!');
            resolve('异步操作1成功回调数据');
        }, 2000);
    });
    return p;
}
function runAsync2(){
    var p = new Promise(function(resolve, reject){
        setTimeout(function(){
            console.log('异步操作2执行完毕!');
            resolve('异步操作2成功回调数据');
        }, 1000);
    });
    return p;
}
Promise
.race([runAsync1(),runAsync2()])
.then(function(results){
    console.log(results);
});

运行结果如下:

可以看到,只要runAsync1、runAsync2中的任何一个率先改变状态,父Promise的状态就跟着改变。而那个率先改变状态的子Promise的返回值,就是传递给父Promise的返回值。

总结

Promise的基本使用方法就是这样,之前看过一篇文章,用做饭(cook)吃饭(eat)洗碗(wash)为例,形象地演示了Promise的用法,地址戳戳戳这里