nodejs之promise详解

详细参考:http://liubin.org/promises-book/#__5



新建一个promise对象:

要想创建一个promise对象、可以使用new来调用Promise的构造器来进行实例化。

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

Instance Method

对通过new生成的promise对象为了设置其值在 resolve(成功) /reject(失败)时调用的回调函数可以使用promise.then() 实例方法。

promise.then(onFulfilled, onRejected)
resolve(成功)时

onFulfilled 会被调用

reject(失败)时

onRejected 会被调用

onFulfilledonRejected 两个都为可选参数。

promise.then 成功和失败时都可以使用。另外在只想对异常进行处理时可以采用promise.then(undefined, onRejected) 这种方式,只指定reject时的回调函数即可。不过这种情况下promise.catch(onRejected) 应该是个更好的选择。


Static Method

Promise 这样的全局对象还拥有一些静态方法。

包括 Promise.all() 还有 Promise.resolve() 等在内,主要都是一些对Promise进行操作的辅助方法。


静态方法Promise.resolve(value) 可以认为是new Promise() 方法的快捷方式。

比如 Promise.resolve(42); 可以认为是以下代码的语法糖。

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

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

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

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


Promise.all 接收一个 promise对象的数组作为参数,当这个数组里的所有promise对象全部变为resolve或reject状态的时候,它才会去调用 .then 方法。

数组里的方法会同时开始执行,而且每个promise的结果(resolve或reject时传递的参数值),和传递给Promise.all 的promise数组的顺序是一致的。

也就是说,这时候 .then 得到的promise数组的执行结果的顺序是固定的,即 [comment, people]。


接着我们来看看和 Promise.all 类似的对多个promise对象进行处理的 Promise.race 方法。

区别:

Promise.all 在接收到的所有的对象promise都变为 FulFilled 或者 Rejected 状态之后才会继续进行后面的处理,与之相对的是Promise.race 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理。


then(onFulfilled, onRejected)和catch的不同:

  1. 使用promise.then(onFulfilled, onRejected) 的话

    • onFulfilled 中发生异常的话,在 onRejected 中是捕获不到这个异常的。

  2. promise.then(onFulfilled).catch(onRejected) 的情况下

    • then 中产生的异常能在 .catch 中捕获

  3. .then.catch 在本质上是没有区别的

    • 需要分场合使用。


Deferred和Promise的关系

简单来说,Deferred和Promise具有如下的关系。

  • Deferred 拥有 Promise

  • Deferred 具备对 Promise的状态进行操作的特权方法

  • 所谓的能对Promise状态进行操作的特权方法,指的就是能对promise对象的状态进行resolve、reject等调用的方法,而通常的Promise的话只能在通过构造函数传递的方法之内对promise对象的状态进行操作。

function Deferred() {
    this.promise = new Promise(function (resolve, reject) {
        this._resolve = resolve;
        this._reject = reject;
    }.bind(this));
}
Deferred.prototype.resolve = function (value) {
    this._resolve.call(this.promise, value);
};
Deferred.prototype.reject = function (reason) {
    this._reject.call(this.promise, reason);
};
function getURL(URL) {
    var deferred = new Deferred();
    var req = new XMLHttpRequest();
    req.open('GET', URL, true);
    req.onload = function () {
        if (req.status === 200) {
            deferred.resolve(req.responseText);
        } else {
            deferred.reject(new Error(req.statusText));
        }
    };
    req.onerror = function () {
        deferred.reject(new Error(req.statusText));
    };
    req.send();
    return deferred.promise;
}
// 运行示例
var URL = "http://httpbin.org/get";
getURL(URL).then(function onFulfilled(value){
    console.log(value);
}).catch(console.error.bind(console));

我们发现它们有如下不同。

  • Deferred 的话不需要将代码用Promise括起来

    • 由于没有被嵌套在函数中,可以减少一层缩进

    • 反过来没有Promise里的错误处理逻辑

在以下方面,它们则完成了同样的工作。

  • 整体处理流程

    • 调用 resolvereject 的时机

  • 函数都返回了promise对象

由于Deferred包含了Promise,所以大体的流程还是差不多的,不过Deferred有用对Promise进行操作的特权方法,以及高度自由的对流程控制进行自由定制。

比如在Promise一般都会在构造函数中编写主要处理逻辑,对 resolvereject 方法的调用时机也基本是很确定的。

new Promise(function (resolve, reject){
    // 在这里进行promise对象的状态确定
});

而使用Deferred的话,并不需要将处理逻辑写成一大块代码,只需要先创建deferred对象,可以在任何时机对 resolvereject 方法进行调用。

var deferred = new Deferred();

// 可以在随意的时机对 `resolve`、`reject` 方法进行调用

换句话说,Promise代表了一个对象,这个对象的状态现在还不确定,但是未来一个时间点它的状态要么变为正常值(FulFilled),要么变为异常值(Rejected);而Deferred对象表示了一个处理还没有结束的这种事实,在它的处理结束的时候,可以通过Promise来取得处理结果。

如果各位读者还想深入了解一下Deferred的话,可以参考下面的这些资料。

  • Promise & Deferred objects in JavaScript Pt.1: Theory and Semantics.

  • Twisted 入门 — Twisted Intro

  • Promise anti patterns · petkaantonov/bluebird Wiki

  • Coming from jQuery · kriskowal/q Wiki


什么是 Promise.prototype.done ?

如果你使用过其他的Promise实现类库的话,可能见过用done代替then的例子。

这些类库都提供了 Promise.prototype.done 方法,使用起来也和 then 一样,但是这个方法并不会返回promise对象。

使用Done例子

if (typeof Promise.prototype.done === 'undefined') {
    Promise.prototype.done = function (onFulfilled, onRejected) {
        this.then(onFulfilled, onRejected).catch(function (error) {
            setTimeout(function () {
                throw error;
            }, 0);
        });
    };
}
var promise = Promise.resolve();
promise.done(function () {
    JSON.parse('this is not json');    // => SyntaxError: JSON.parse
});

从上面我们可以看出,两者之间有以下不同点。

  • done 并不返回promise对象

    • 也就是说,在done之后不能使用 catch 等方法组成方法链

  • done 中发生的异常会被直接抛给外面

    • 也就是说,不会进行Promise的错误处理(Error Handling)

由于done 不会返回promise对象,所以我们不难理解它只能出现在一个方法链的最后。


使用Promise进行顺序(sequence)处理


  • 循环使用then调用的方法

  • 使用for循环的方法

  • 使用reduce的方法

  • 分离出顺序处理函数的方法


另外提供promise扩展模块Q模块的方法大全文档:https://github.com/kriskowal/q/wiki/API-Reference

                                                               bluebird模块:http://bluebirdjs.com/docs/api-reference.html








你可能感兴趣的:(nodejs之promise详解)