从发布订阅模式入手读懂Node.js的EventEmitter源码
本文会讲解另一种更现代的异步实现方案:Promise。Promise几乎是面试必考点,所以我们不能仅仅会用,还得知道他的底层原理,学习他原理的最好方法就是自己也实现一个Promise。所以本文会自己实现一个遵循Promise/A+规范的Promise。实现之后,我们还要用Promise/A+官方的测试工具来测试下我们的实现是否正确,这个工具总共有872个测试用例,全部通过才算是符合Promise/A+规范,下面是他们的链接:
Promise/A+规范: https://github.com/promises-aplus/promises-spec
Promise/A+测试工具: https://github.com/promises-aplus/promises-tests
本文的完整代码托管在GitHub上:
Promise用法Promise的基本用法,网上有很多,我这里简单提一下,我还是用三个相互依赖的网络请求做例子,假如我们有三个网络请求,请求2必须依赖请求1的结果,请求3必须依赖请求2的结果,如果用回调的话会有三层,会陷入“回调地狱”,用Promise就清晰多了:
const request = require("request"); // 我们先用Promise包装下三个网络请求 // 请求成功时resolve这个Promise const request1 = function() { const promise = new Promise((resolve) => { request('https://www.baidu.com', function (error, response) { if (!error && response.statusCode == 200) { resolve('request1 success'); } }); }); return promise; } const request2 = function() { const promise = new Promise((resolve) => { request('https://www.baidu.com', function (error, response) { if (!error && response.statusCode == 200) { resolve('request2 success'); } }); }); return promise; } const request3 = function() { const promise = new Promise((resolve) => { request('https://www.baidu.com', function (error, response) { if (!error && response.statusCode == 200) { resolve('request3 success'); } }); }); return promise; } // 先发起request1,等他resolve后再发起request2, // 然后是request3 request1().then((data) => { console.log(data); return request2(); }) .then((data) => { console.log(data); return request3(); }) .then((data) => { console.log(data); })上面的例子里面,then是可以链式调用的,后面的then可以拿到前面resolve出来的数据,我们控制台可以看到三个success依次打出来:
Promises/A+规范通过上面的例子,其实我们已经知道了一个promise长什么样子,Promises/A+规范其实就是对这个长相进一步进行了规范。下面我会对这个规范进行一些讲解。
术语
promise:是一个拥有 then 方法的对象或函数,其行为符合本规范
thenable:是一个定义了 then 方法的对象或函数。这个主要是用来兼容一些老的Promise实现,只要一个Promise实现是thenable,也就是拥有then方法的,就可以跟Promises/A+兼容。
value:指reslove出来的值,可以是任何合法的JS值(包括 undefined , thenable 和 promise等)
exception:异常,在Promise里面用throw抛出来的值
reason:拒绝原因,是reject里面传的参数,表示reject的原因
Promise状态Promise总共有三个状态:
pending: 一个promise在resolve或者reject前就处于这个状态。
fulfilled: 一个promise被resolve后就处于fulfilled状态,这个状态不能再改变,而且必须拥有一个不可变的值(value)。
rejected: 一个promise被reject后就处于rejected状态,这个状态也不能再改变,而且必须拥有一个不可变的拒绝原因(reason)。
注意这里的不可变指的是===,也就是说,如果value或者reason是对象,只要保证引用不变就行,规范没有强制要求里面的属性也不变。Promise状态其实很简单,画张图就是:
then方法一个promise必须拥有一个then方法来访问他的值或者拒绝原因。then方法有两个参数:
promise.then(onFulfilled, onRejected) 参数可选onFulfilled 和 onRejected 都是可选参数。
如果 onFulfilled 不是函数,其必须被忽略
如果 onRejected 不是函数,其必须被忽略
onFulfilled 特性如果 onFulfilled 是函数:
当 promise 执行结束后其必须被调用,其第一个参数为 promise 的终值value
在 promise 执行结束前其不可被调用
其调用次数不可超过一次
onRejected 特性如果 onRejected 是函数:
当 promise 被拒绝执行后其必须被调用,其第一个参数为 promise 的据因reason
在 promise 被拒绝执行前其不可被调用
其调用次数不可超过一次
多次调用then 方法可以被同一个 promise 调用多次
当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
当 promise 被拒绝执行时,所有的 onRejected 需按照其注册顺序依次回调
返回then 方法必须返回一个 promise 对象。
promise2 = promise1.then(onFulfilled, onRejected);如果 onFulfilled 或者 onRejected 返回一个值 x ,则运行 Promise 解决过程:[[Resolve]](promise2, x)
如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
如果 onFulfilled 不是函数且 promise1 成功执行, promise2 必须成功执行并返回相同的值
如果 onRejected 不是函数且 promise1 拒绝执行, promise2 必须拒绝执行并返回相同的据因