ECMAScript6基础学习教程(八)Promise

JavaScript被设计为单线程(webWoker可以处理多线程),利用事件轮询机制,可以模拟出多线程效果,也就是异步操作,而回调函数callback是事件轮询调用的目标方法。

但是,通过回调函数处理异步事件有很多不确定性,并且容易陷入“回调地狱”-嵌套太深。于是,Promise概念被提出,并且很多JavaScript框架(比如JQuery)支持的异步API都基于Promise理念构建的。

1.什么是Promise?

Promise是一个对象,用来传递异步操作的信息,它代表了某个未来时刻才知道结果的事件,并且这个事件提供统一API接口。

2. Promise对象

Promise原型对象提供的主要方法有:

//添加状态改变时的回调函数
Promise.prototype.then(resolvedFunc, rejectedFunc) 
//响应rejected状态的promise(如果前面有错误抛出,会产生一个rejected状态的promise)
//相当于promise.then(null, rejectedFunc)
Promise.prototype.catch(rejectedFunc) 

参考一个标准的Promise实例:

var promise = new Promise(function(resolve, reject){
  // 你的代码---pending状态
  if (isSuccessful) {
    resolve(value); // 成功
  }
  else {
    reject(error); // 失败
  }
// 这里的代码永远不会被运行
});

promise
.then(
  function(value){//resolved时调用}, 
  function(error){// rejected时调用})
.catch(function(error){//有错误抛出时调用});

promise对象有如下特点:

  1. 可以利用promise对象创建一个异步操作。
  2. 有三种状态:pending, resolved和rejected。异步代码运行时为pending,运行后的结果只会是两种:成功-resolved,或者失败-rejected。状态变化是单行流动,不可逆转。
  3. 在一个promise里,resolve或者reject方法只会被调用一次。
  4. resolve()/reject()可以利用参数传递数据,但是,只支持传递第一个参数。也就是说,promise决议只能传递单个值/对象。因此,实际应用中,需要将多个值封装在一个对象中传递
  5. then()和catch()函数都会默认返回一个promise对象。
  6. 如果没有给then()传递函数作为完成处理函数参数,还是会有替代的默认处理函数,并且,该默认函数会把接受到的值传递给下一个promise对象。
getPromise(40, true).
    then(null,null).then(function (value) {
    console.log("resolved:"+value);
});
// 打印:resolved:40
// 可见,如果不设置then的处理函数参数,resolved值40一直会被传递下去。

3. 一个Promise实例

下面是一个Promise例子,参考注释。
创建promise的工厂方法:

var getPromise = function (val, isSuccessful) {
    var promise = new Promise(function (resolve, reject) {
        setTimeout(function () {
            if (isSuccessful) {
                // 决议成功,回调then()的第一个函数参数
                resolve(val); 
            }
            else {
                // 决议失败,回调then()的第二个函数参数
                reject(new Error("oh, error!")); 
            }
        }, 0);
    });
    return promise;
};

第一次测试(连续调用两次promise):

getPromise(30, true).then((value) => {
    console.log("the first resolved status: " + value);
    // 返回一个promise对象,该promise决议结果会决定下一个then()函数应该调用哪个回调函数
    // 如果不显性返回promise对象,ES6会默认创建一个空值promise对象最为返回值
    return getPromise(20, true);
}, (error) => {
    console.log("the second rejected status: " + error);
}).then((value) => {
    console.log("the second resolved status: " + value);
    // 一个rejected状态的promise对象被返回
    //由于后续没有then(),因此catch函数捕获错误状态
    throw new Error('create a error!');
}, (error) => {
    console.log("the second rejected status: " + error);
}).catch((error) => {
    console.log("Catch: " + error);
});

//运行结果:
the first resolved status: 30
the second resolved status: 20
Catch: Error: create a error!

第二次测试(多加一个then调用):

getPromise(30, true).then((value) => {
    console.log("the first resolved status: " + value);
    return getPromise(20, true);
}, (error) => {
    console.log("the second rejected status: " + error);
}).then((value) => {
    console.log("the second resolved status: " + value);
    // 一个rejected状态的promise对象被返回
    //由于后续有then(),因此then函数的第二个回调函数被运行
    throw new Error('create a error!');
}, (error) => {
    console.log("the second rejected status: " + error);
}).then((value) => {
    console.log("the third resolved status: " + value);
}, (error) => {
    console.log("the third rejected status: " + error);
}).catch((error) => {
    console.log("Catch: " + error);
});

// 运行结果:
the first resolved status: 30
the second resolved status: 20
the third rejected status: Error: create the first error!

第三次测试(修改getPromise函数):

var getPromise = function (val, isSuccessful) {
    var promise = new Promise(function (resolve, reject) {
         if (isSuccessful) {
                resolve(val);
                //resolve之后抛出错误,是不会被捕获的
                throw new Error('error, error, error!');
            }
            else {
                reject(new Error("oh, error!"));
            }
    });
    return promise;
};
// 测试1和测试2的运行结果不会改变

从上面的例子可以看到,可以用同步书写方式连续调用多个异步请求。并且,Promise还提供了静态函数帮助解决更复杂的异步编程场景。

4. Promise静态方法

Promise提供的静态方法有:

Promise.all([promise1, promise2 [,promiseN]])
Promise.race([promise1, promise2 [,promiseN])
Promise.resolve()
Promise.reject()
(1) Promise.all()

将多个promise实例包装成一个新的promise对象,只有多个promise的状态都为resolved,Promise.all()的状态才会变为resolved。
继续上面的例子,测试如下:

Promise.all([
    getPromise(20, true),
    getPromise(30, true), 
    getPromise(40, true)])
    .then(function (value) {
        console.log(value);
    });
// 输出为:[ 20, 30, 40 ]

每个Promise实例的resolved值都会暂存在一个数组里,最后,该数组被传递到Promise.all()的resolved回调函数。

(2) Promise.race()

将多个promise实例包装成一个新的promise对象,只有第一个promise状态为resolved时,Promise.race()的状态才变为resolved

并且,第一个promise的值会传递给Promise.race()。

基于上面例子继续测试:

Promise.race([
    getPromise(20, true),
    getPromise(30, false),
    getPromise(40, false)])
    .then(function (value) {
        console.log(value);
    }, function (error) {
        console.log(error);
    });
//输出为: 20

Promise.race([
    getPromise(20, false),
    getPromise(30, true),
    getPromise(40, false)])
    .then(function (value) {
        console.log(value);
    }, function (error) {
        console.log(error);
    });
// 输出为: [Error: oh, error!]
(3) Promise.resolve()

该方法会返回一个Promise对象,情况分为两种:

  • 如果目标对象不是Promise对象,该方法会创建一个Promise对象
  • 如果目标对象本身就是Promise对象,该方法会将这个Promise对象直接返回
// p2和p1行为完全一样
var p1 = new Promise(function(resolve, reject){
  resolve(40);
});
var p2 = Promise.resolve(40);
// 向Promise.resolve()传递一个Promise对象,则直接返回这个对象
var p3 = Promise.resolve(p2);
console.log(p2===p3); // true
(4) Promise.reject()

返回一个新的Promise实例,状态为rejected:

var p = new Promise(function(resolve, reject){
  reject("error");
}); 

//可以用 Promise.reject()快速创建promise对象,状态为rejected
var p = Promise.reject("error");
p.then(null, function(error) {
// 回调函数被调用
});

微信公众号:

你可能感兴趣的:(ECMAScript6基础学习教程(八)Promise)