Promise

这样一个场景,两个异步请求,第二个需要用到第一个请求成功的数据
ajax({
        url: url1,
        success: function(data) {
            ajax({
                url: url2,
                data: data,
                success: function() {
                }
            });
        }
    });

如果继续下去在回调函数中进行下一步操作,嵌套的层数会越来越多。我们可以进行适当的改进,把回调函数写到外面

function A() {
        ajax({
            url: url1,
            success: function(data) {
                B(data);
            }
        });
    }
    function B(data) {
        ajax({
            url: url2,
            success: function(data) {
                ......
            }
        });
    }

即使是改写成这样,代码还是不够直观,如果出现多层回调嵌套,也就是我们常说的厄运的回调金字塔(Pyramid of Doom)。有了Promise对象,代码就可以写得非常清晰

new Promise(A).done(B);

Promises 将嵌套的 callback,改造成一系列的.then的连缀调用,去除了层层缩进的糟糕代码风格。Promises 不是一种解决具体问题的算法,而已一种更好的代码组织模式。

各个语言平台都有相应的 Promise 实现

  • Java's java.util.concurrent.Future
  • Python's Twisted deferreds and PEP-3148 futures
  • F#'s Async
  • .Net's Task
  • C++ 11's std::future
  • Dart's Future
  • Javascript's Promises/A/B/D/A+

 

var val = 1;
// 我们假设step1, step2, step3都是ajax调用后端或者是
// 在Node.js上查询数据库的异步操作
// 每个步骤都有对应的失败和成功处理回调
// 需求是这样,step1、step2、step3必须按顺序执行
function step1(resolve, reject) {
    console.log('步骤一:执行');
    if (val >= 1) {
        resolve('Hello I am No.1');
    } else if (val === 0) {
        reject(val);
    }
}

function step2(resolve, reject) {
    console.log('步骤二:执行');
    if (val === 1) {
        resolve('Hello I am No.2');
    } else if (val === 0) {
        reject(val);
    }
}

function step3(resolve, reject) {
    console.log('步骤三:执行');
    if (val === 1) {
        resolve('Hello I am No.3');
    } else if (val === 0) {
        reject(val);
    }
}

new Promise(step1).then(function(data){
    console.info(data);
    return new Promise(step2);
},function(data){
    console.log("执行reject方法");
    return ;
}).then(function(data){
    console.info(data);
    return new Promise(step3);
}).then(function(data){
    console.info(data);
    return data;//返回“Hello I am No.3”
}).then(function(data){
    console.info(data);
    return data;
});

输出

步骤一:执行
Hello I am No.1
步骤二:执行
Hello I am No.2
步骤三:执行
Hello I am No.3
Hello I am No.3

 

Promise的意义就在于 then 链式调用 ,它避免了异步函数之间的层层嵌套,将原来异步函数的嵌套关系 转变为便于阅读和理解的 链式步骤关系 。
Promise构造函数只接受一个参数,即带有异步逻辑的函数。这个函数在 new Promise 时已经执行了。只不过在没有调用 then 之前不会 resolve 或 reject。
在then方法中通常传递两个参数,一个 resolve 函数,一个 reject 函数。reject就是出错的时候运行的函数罢。resolve 函数必须返回一个值才能把链式调用进行下去。
  • resolve 返回一个新 Promise,返回一个新Promise之后再调用的then就是新Promise中的逻辑了。
  • resolve 返回一个值,返回一个值会传递到下一个then的resolve方法参数中。

基本的 api

  1. Promise.resolve()
  2. Promise.reject()
  3. Promise.prototype.then()
  4. Promise.prototype.catch()
  5. Promise.all() // 所有的完成 
  6. Promise.race() // 竞速,完成一个即可
Promise 构造函数接受一个函数作为参数,该函数的两个参数分别是 resolve 方法和 reject 方法。
如果异步操作成功,则用 resolve 方法将 Promise 对象的状态,从「未完成」变为「成功」(即从 pending 变为 resolved);
如果异步操作失败,则用 reject 方法将 Promise 对象的状态,从「未完成」变为「失败」(即从 pending 变为 rejected)。
 
catch的用法,它和then的第二个参数一样,用来指定reject的回调
getNumber()
.then(function(data){
    console.log('resolved');
    console.log(data);
})
.catch(function(reason){
    console.log('rejected');
    console.log(reason);
});

效果和写在then的第二个参数里面一样。不过它还有另外一个作用:在执行resolve的回调(也就是上面then中的第一个参数)时,如果抛出异常了(代码出错了),那么并不会报错卡死js,而是会进到这个catch方法中。

 
Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
Promise
.all([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});
用Promise.all来执行,all接收一个数组参数,里面的值最终都算返回Promise对象。这样,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。那么,三个异步操作返回的数据哪里去了呢?都在then里面呢,all会把所有异步操作的结果放进一个数组中传给then,就是上面的results。
race的用法,all方法的效果实际上是「谁跑的慢,以谁为准执行回调」,那么相对的就有另一个方法「谁跑的快,以谁为准执行回调」,这就是race方法,这个词本来就是赛跑的意思。
 
Promise
.race([runAsync1(), runAsync2(), runAsync3()])
.then(function(results){
    console.log(results);
});

在then里面的回调开始执行时,runAsync2()和runAsync3()并没有停止,仍旧再执行。于是再过1秒后,输出了他们结束的标志。

Promise 对象有以下两个特点。
(1)对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:Pending(进行中)、Resolved(已完成,又称 Fulfilled)和 Rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
(2)一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 Pending 变为 Resolved 和从 Pending 变为 Rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。
 
有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
Promise 也有一些缺点。首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

 

参考:

 

 

posted @ 2016-03-23 23:06  圣耀  阅读(195)  评论(0编辑  收藏  举报