第11章 Promises

Promise

promises是为了解决异步编程中回调函数存在的一些问题而产生的,它不是用了取代回调函数的,而是使得回调函数更加的好用。Node.js使得异步编程更加的流行,promise的用处也得到了很好的体现。

  1. 基本语法;
  2. Promise状态和Promise静态方法 Promise.resolve(), Promise.reject();
  3. 异常处理,通过事件的方式;
  4. 另外2个静态方法: Promise.all(), Promise.race()

一.基本语法

1.异步编程的几种模式

我们知道javascript是单线程的,所有要执行的代码都放在队列中,称之为job queue.
一般js异步模式可以分为: callback模式, promise模式, generator模式

2.什么是promise

promise可以称为future,是一个异步操作的占位符,首先它是一个对象。它有2种状态状态:

  1. unsettled状态: 表示异步操作还未完成,内部属性[[PromiseState]]设置为 pending;
  2. settled状态: 表示异步操作完成,成功: 内部属性[[PromiseState]]设置为 fulfilled; 失败rejected.

3.then()方法

当promise完成操作之后,可以通过 then()方法进行操作,then语法:

then(fulfillmentCallback, rejectCallback)

第一个回调表示成功之后执行,第二个回调表示失败后执行,这两个回调只会执行一个,也可以分开写:

then(fulfillmentCallback);

then(null, rejectCallback);
// 等价于
catch(rejectCallback)

4.thenable对象

一个对象拥有一个then()方法,我们称这个对象为 thenable对象all promises are thenable, but all thenables are not promises

// 返回一个对象,包含一个then方法
function foo(x) {
    return {
        then: function(resolve, reject) {
            setTimeout(resolve, 0, "回掉函数"+x);
            return this;
        }
    };
}
Promise.all([foo(1), foo(2)]).then(function(args) {
    console.log(args);
})

// ["回调参数1", "回调参数2"]

二.创建一个promise

1.创建一个 Unsettled Promises, 使用构造器

新的promises通过Promise构造函数创建,此时promise状态为pending, 这个构造函数接受唯一一个参数: 一个function,我们称之为 executor (这个executor不是异步的,调用时会立即执行).

而这个executor包含2个参数: 2个函数 resolve(), reject()。当executor成功执行,会调用resolve;当executor执行失败,会调用rejected()

下面以Node.js读取文件为例子:

let fs = requrie("fs");

function readFile(filename) {
    return new Promise(function(resolve, reject) {
        fs.readFile(filename, {encoding: "utf8"}, function(err, contents) {
            // Node.js 的 `error-first`模式
            // 失败调用reject
            if (err) {
                reject(err);
                return;
            }
            // 成功时调用resolve
            resolve(contents);
        });
    });
}

let promise = readFile("a.txt");
promise.then(function(contents) {
    console.log(contents);
}).catch(function(err) {
    console.log(err.message);
})

2.创建 Settled promises: Promise.resolve(), Promise.reject()

通过构造器我们可以创建 unsettled promises, 我们可以通过静态方法 Promise.resolve(),Promise.rejected()创建 settled promises.

  • Promise.resolve(): 接受一个参数,返回一个promise,在 fulfilled 状态
  • Promise.reject(): 接受一个参数,返回一个promise, 在 rejected 状态

1.Promise.resolve():

添加一个fulfillment handler来接受返回的promise中的数据

var promiseResolve = Promise.resolve(42);
promiseResolve.then(function(value) {
    console.log(value);      // 42
})

如果reject handler添加给这个promise,则这个handler将永远不会调用,因为一但promise状态确定将不会更改,即:

var promiseResolve = Promise.revolve(42);
// 这个handler永远不会执行
promiseRevolve.catch(function(err) {
    console.log(err);
});

2.Promise.reject():

添加一个 rejected handler:

var promiseRejected = Promise.reject(42);
promiseRejected.catch(function(value) {
    console.log(value);          // 42
});

3.thenable对象可以作为参数传入Promise.resolve静态方法中,依据thenable方法中调用的函数

此条在chrome中存在问题,待更正, 可以在Node.js中运行

如果thenable中调用resolve则返回的promise为fulfilled状态;如果thenable中调用的是reject则返回的promise是rejected状态:

let thenable1 = {
    then: function(resolve, reject) {
        resolve(42);
    }
};

var p1 = Promise.resolve(thenable1); // "fulfilled"状态
p1.then(function(value) {    // 使用fulfillment handler
    console.log(value); // 42
});

let thenable2 = {
    then: function(resolve, reject) {
        reject(43);
    }
};

// 注意这个地方也是使用Promise.resolve()
var p2 = Promise.resolve(thenable2); // "rejected"状态
p2.catch(function(value) {  // 使用reject handler
    console.log(value); // 43
});             

三.全局Promise Rejection 处理

promise最有争议的地方就是当一个promise失败但是没有rejection handler处理错误时静默失败。不过浏览器和Node.js都有相应的处理机制,两者大同小异,都是通过事件的方式监听。

  1. Node.js Rejection Handling

两个事件:

  1. unhandledRejection:当promise失败(rejected),但又没有处理时触发,event handler 有2个参数: reason,promise;
  2. rejectionHandled: 当promise失败(rejected),被处理时触发,hanler 有1个参数: promise;
let rejected;
process.on("unhandledRejection", function(reason, promise) {
    console.log(reason.message); // "boom"
    console.log(rejected === promise); // true
});
rejected = Promise.reject(new Error("boom")); // 出现reject, 没有catch处理

处理:

let rejected;
process.on("rejectionHandled", function(promise) {
    console.log(rejected === promise); // true
});
rejected = Promise.reject(new Error("boom"));

// 等待添加rejection handler
setTimeout(function() {
    rejected.catch(function(value) {    // 处理rejection
        console.log(value.message); // "boom"
    });
}, 1000);

将可能出现的rejection存储在map中,然后检查是否存在相应的handler:

let rejections = new Map();

// 当一个rejection出现,添加到map中
process.on("unhandledRejection", function(reason, promise) {
    rejections.set(promise, reason);
});

// 存在handler,则将其从map集合中删除
process.on("rejectionHanled", function(promise) {
    rejections.delete(promise);
});
// 间隔一段时间对集合中的查看处理,清空
setInterval(function() {
    rejections.forEach(function(reason, promise) {
        console.log(reason.message ? reason.message : reason);
    
        // 用某种方法处理这些rejections
        handleRejection(promise, reason);
    });
    // 清空集合
  rejections.clear();
}, 60000);

2.浏览器rejection处理

浏览器和Node.js处理大同小异,通过事件:

  1. unhandledRejection
  2. rejectionhandled

这2个事件的 event 对象都包含3个属性: type, promise, reason(这点和Node.js不同)

处理方式:

var rejections = new Map();
window.onunhandledrejection = function(event) {
    rejections.set(event.promise, event.reason);
}
window.onrejectionhandled = function(event) {
    rejections.delete(event.promise);
}
setInterval(function() {
    rejections.forEach(function(event) {
        console.log(event.reason.message ? event.reason.message : event.reason);
        
        // 处理
        handleRejection(event.promise, event.reason);
    });
    rejections.clear();
}, 60000);

四.Promise.all() Promise.race()

1.Promise.all()

这个静态方法接受一个可迭代的promises(比如数组),当数组中的每个promise都为fulfilled状态时,返回一个promise.当失败时,立马结束而不等待其他promise完成

let p1 = new Promise((resolve, reject) => {resolve(42)});
let p2 = new Promise((resolve, reject) => {resolve(43)});
let p3 = new Promise((resolve, reject) => {resolve(44)});
let p4 = Promise.all([p1, p2, p3]);
p4.then(function(value) {
    console.log(Array.isArray(value));  // true value为一个数组
    console.log(value[0]); // 42
    console.log(value[1]); // 43
    console.log(value[2]); // 44
});

其中也promise 状态为rejected:

let p1 = new Promise((resolve, reject) => {resolve(42)});
let p2 = new Promise((resolve, reject) => {reject(43)}); // reject
let p3 = new Promise((resolve, reject) => {resolve(44)});
let p4 = Promise.all([p1, p2, p3]);
p4.catch(function(value) {
    console.log(Array.isArray(value)); // false 
    console.log(value);  // 43
})

当有promise被rejected的时候,value永远为单个值,而不是一个数组

2.Promise.race()

返回一个合适的promise,只要数组中任何promise先完成了。如果先fulfilled,则返回的promise为fulfilled状态; 如果一个promise 先 reject,则返回的promise为 rejected状态。

let p1 = new Promise((resolve, reject) => resolve(42));
let p2 = Promise.reject(43); // 直接为rejected状态
let p3 = new Promise((resolve, reject) => resolve(44));
let p4 = Promise.race(function(value) {
    console.log(value); // 43
});

总结

上面讲到的都是一些关于promise的基本概念,而没有深入的研究,比说promise chaining, promise如何处理异步任务等等,均未涉及,这些内容还需要我以后更深入的学习总结,今天到此为止,推荐一篇讲解比较的文章。

promise 详细文档

2016/9/21 17:47:43

你可能感兴趣的:(第11章 Promises)