浅谈ES6原生Promise

ES6标准出炉以前,一个幽灵,回调的幽灵,游荡在JavaScript世界。前端

正所谓:node

世界本没有回调,写的人多了,也就有了})})})})})git

Promise的兴起,是由于异步方法调用中,每每会出现回调函数一环扣一环的状况。这种状况致使了回调金字塔问题的出现。不只代码写起来费劲又不美观,并且问题复杂的时候,阅读代码的人也难以理解。
举例以下:程序员

db.save(data, function(data){
    // do something...
    db.save(data1, function(data){
        // do something...
        db.save(data2, function(data){
            // do something...
            done(data3); // 返回数据
        })
    });
});

假设有一个数据库保存操做,一次请求须要在三个表中保存三次数据。那么咱们的代码就跟上面的代码类似了。这时候假设在第二个db.save出了问题怎么办?基于这个考虑,咱们又须要在每一层回调中使用相似try...catch这样的逻辑。这个就是万恶的来源,也是node刚开始广为诟病的一点。github

另一个缺点就是,假设咱们的三次保存之间并无先后依赖关系,咱们仍然须要等待前面的函数执行完毕, 才能执行下一步,而没法三个保存并行,以后返回一个三个保存事后须要的结果。(或者说实现起来须要技巧)数据库

不幸的是,在我刚开始接触node的时候,我写了大量这样的hell。segmentfault

做为一个有时还动下脑子的程序员,我尝试了朴灵大人的eventproxy。后来由于仍是写前端代码多一些,我接触了ES6,发现了一个解决回调深渊的利器Promise数组

其实早在ES6的Promise以前,Qwhen.jsbluebird等等库早就根据Promise标准(参考Promise/A+)造出了本身的promise轮子。
(看过一篇文章,我以为颇有道理。里面说,不要扩展内置的原生对象。这种作法是不能面向将来的。因此这里有一个提示:使用扩展原生Promise的库时,须要谨慎。)promise

这里仅讨论原生的Promise并发

ES6 Promise

Promise对象状态

在详解Promise以前,先来点理论:

Promise/A+规范, 规定Promise对象是一个有限状态机。它三个状态:

  • pending(执行中)

  • fulfilled(成功)

  • reject(拒绝)

其中pending为初始状态,fulfilled和rejected为结束状态(结束状态表示promise的生命周期已结束)。

状态转换关系为:

pending->fulfilled,pending->rejected。

随着状态的转换将触发各类事件(如执行成功事件、执行失败事件等)。

Promise形式

Promise的长相就像这样子:

var promise = new Promise(function func(resolve, reject){
    // do somthing, maybe async
    if (success){
      return resolve(data);
    } else {
      return reject(data);
    }
});

promise.then(function(data){
    // do something... e.g
    console.log(data);
}, function(err){
    // deal the err.
})

这里的变量promisePromise这个对象的实例。

promise对象在建立的时候会执行func函数中的逻辑。

逻辑处理完毕而且没有错误时,resolve这个回调会将值传递到一个特殊的地方。这个特殊的地方在哪呢?就是下面代码中的then,咱们使用then中的回调函数来处理resolve后的结果。好比上面的代码中,咱们将值简单的输出到控制台。若是有错误,则rejectthen的第二个回调函数中,对错误进行处理。

配合上面的有限状态机的理论,咱们知道在Promise构造函数中执行回调函数代码时,状态为pendingresolve以后状态为fulfilledreject以后状态为reject

Promise数据流动

以上是promise的第一次数据流动状况。

比较funny的是,promise的then方法依然可以返回一个Promise对象,这样咱们就又能用下一个then来作同样的处理。

第一个then中的两个回调函数决定第一个then返回的是一个什么样的Promise对象。

  • 假设第一个then的第一个回调没有返回一个Promise对象,那么第二个then的调用者仍是原来的Promise对象,只不过其resolve的值变成了第一个then中第一个回调函数的返回值。

  • 假设第一个then的第一个回调函数返回了一个Promise对象,那么第二个then的调用者变成了这个新的Promise对象,第二个then等待这个新的Promise对象resolve或者reject以后执行回调。

话虽然饶了一点,可是我自我感受说的仍是很清楚的呢。哈哈~

若是任意地方遇到了错误,则错误以后交给遇到的第一个带第二个回调函数的then的第二个回调函数来处理。能够理解为错误一直向后reject, 直到被处理为止。

另外,Promise对象还有一个方法catch,这个方法接受一个回调函数来处理错误。即:

promise.catch(function(err){
    // deal the err.
})

假设对错误的处理是类似的,这个方法能够对错误进行集中统一处理。因此其余的then方法就不须要第二个回调啦~

控制并发的Promise

Promise有一个"静态方法"——Promise.all(注意并不是是promise.prototype), 这个方法接受一个元素是Promise对象的数组。

这个方法也返回一个Promise对象,若是数组中全部的Promise对象都resolve了,那么这些resolve的值将做为一个数组做为Promise.all这个方法的返回值的(Promise对象)的resolve值,以后能够被then方法处理。若是数组中任意的Promisereject,那么该reject的值就是Promise.all方法的返回值的reject值.

很op的一点是:
then方法的第一个回调函数接收的resolve值(如上所述,是一个数组)的顺序和Promise.all中参数数组的顺序一致,而不是按时间顺序排序。

还有一个和Promise.all相相似的方法Promise.race,它一样接收一个数组,只不过它只接受第一个被resolve的值。

将其余对象变为Promise对象

Promise.resovle方法,能够将不是Promise对象做为参数,返回一个Promise对象。

有两种情形:

  1. 假设传入的参数没有一个.then方法,那么这个返回的Promise对象变成了resolve状态,其resolve的值就是这个对象自己。

  2. 假设传入的参数带有一个then方法(称为thenable对象), 那么将这个对象的类型变为Promise,其then方法变成Promise.prototype.then方法。

Promise是解决异步的方案吗?

最后说一点很重要的事:Promise的做用是解决回调金字塔的问题,对于控制异步流程实际上没有起到很大的做用。真正使用Promise对异步流程进行控制,咱们还要借助ES6 generator函数。(例如Tj大神co库的实现)。

然而ES7将有一个更加牛逼的解决方案:async/await,这个方案相似于co,可是加了原生支持。拭目以待吧。

文档

mozilla开发者文档


以上。一点微小的看法,谢谢你们。

相关文章
相关标签/搜索