异步编程是前端开发者必需的技能,过去管理异步的主要机制都是经过函数回调,然而会出现像“回调地狱”这样的问题。为了更好的管理回调,ES6
增长了一个新的特性 Promise
。Promise
是 ES7
中 async/await
语法的基础,是 JavaScript
中处理异步的标准形式,现实开发中基本离不开 Promise
了。咱们接下来会根据 Promises/A+
规范本身实现一个 Promise
。前端
完整的代码能够点击个人 github 进行查看,若是你喜欢,欢迎 star
,若是发现有问题或者错误,也欢迎提出来。git
首先咱们来看 Promises/A+
规范的具体内容,Promises/A+
规范能够查看 Promises/A+
,下面是翻译的规范供参考github
术语
promise
是一个有 then
方法的对象或者是函数,行为遵循本规范thenable
是一个有 then
方法的对象或者是函数value
是 promise
状态成功时的值,包括 undefined
、thenable
、promise
exception
是一个使用 throw
抛出的异常值reason
是 promise
状态失败时的值要求
Promise
必须处于如下三个状态之一: pending
, fulfilled
或者 rejected
。npm
2.1.1 若是 promise 在 pending 状态 2.1.1.1 能够变成 fulfilled 或者是 rejected 2.1.2 若是 promise 在 fulfilled 状态 2.1.2.1 不会变成其它状态 2.1.2.2 必须有一个value值 2.1.3 若是 promise 在 rejected 状态 2.1.3.1 不会变成其它状态 2.1.3.2 必须有一个 promise 被 reject 的 reason
promise
必须提供一个 then
方法,来访问最终的结果编程
promise
的 then
方法接收两个参数promise
promise.then(onFulfilled, onRejected)
2.2.1 onFulfilled 和 onRejected 都是可选参数: 2.2.1.1 onFulfilled 必须是函数类型 2.2.1.2 onRejected 必须是函数类型 2.2.2 若是 onFulfilled 是函数: 2.2.2.1 必须在 promise 变成 fulfilled 时,调用 onFulfilled,参数是 promise 的 value 2.2.2.2 在 promise 的状态不是 fulfilled 以前,不能调用 2.2.2.3 onFulfilled 只能被调用一次 2.2.3 若是 onRejected 是函数: 2.2.3.1 必须在promise变成 rejected 时,调用 onRejected,参数是promise的reason 2.2.3.2 在promise的状态不是 rejected 以前,不能调用 2.2.3.3 onRejected 只能被调用一次 2.2.4 onFulfilled 和 onRejected 应该是微任务 2.2.5 onFulfilled 和 onRejected 必须做为函数被调用 2.2.6 then 方法可能被屡次调用 2.2.6.1 若是 promise 变成了 fulfilled 态,全部的 onFulfilled 回调都须要按照 then 的顺序执行 2.2.6.2 若是 promise 变成了 rejected 态,全部的 onRejected 回调都须要按照 then 的顺序执行 2.2.7 then 必须返回一个 promise, promise2 = promise1.then(onFulfilled, onRejected); 2.2.7.1 onFulfilled 或 onRejected 执行的结果为 x, 执行 resolutionProcedure(promise2, x) 2.2.7.2 若是 onFulfilled 或者 onRejected 执行时抛出异常e, promise2 须要被 reject 2.2.7.3 若是 onFulfilled 不是一个函数,promise2 以 promise1 的值 fulfilled 2.2.7.4 若是 onRejected 不是一个函数,promise2 以 promise1 的 reason rejected
resolutionProcedure(promise2, x, resolve, reject)
2.3.1 若是 promise2 和 x 相等,那么 promise 执行 reject TypeError 2.3.2 若是 x 是一个 promsie 2.3.2.1 若是 x 是 pending 状态,那么 promise 必需要保持 pending 状态直到 x 变成 fulfilled 或者 rejected 状态 2.3.2.2 若是 x 是 fulfilled 状态, fulfill promise with the same value 2.3.2.3 若是 x 是 rejected 状态, reject promise with the same reason 2.3.3 若是 x 是一个 object 或者 是一个 function 2.3.3.1 let then = x.then. 2.3.3.2 若是 x.then 这步出错,那么 reject promise with e as the reason 2.3.3.3 若是 then 是一个函数,then.call(x, resolvePromise, rejectPromise) 2.3.3.3.1 resolvePromiseFn 的 入参是 y, 执行 resolutionProcedure(promise2, y, resolve, reject); 2.3.3.3.2 rejectPromise 的 入参是 r, reject promise with r. 2.3.3.3.3 若是 resolvePromise 和 rejectPromise 都调用了,那么第一个调用优先,后面的调用忽略。 2.3.3.3.4 若是调用then抛出异常 e 2.3.3.3.4.1 若是 resolvePromise 或 rejectPromise 已经被调用,那么忽略 2.3.3.3.4.2 不然,reject promise with e as the reason 2.3.3.4 若是 then 不是一个function. fulfill promise with x. 2.3.4 若是 x 不是一个 object 或者 function,fulfill promise with x.
接下来咱们只要按照规范来实现 Promise
就好了,很显然规范后半部分看起来是比较复杂的,尤为是 resolutionProcedure
函数的实现,但其实这只是一些实现的细节而已。初看可能不是那么顺畅,那么强烈建议多看几遍规范,而后本身多实现几遍。缓存
promise
须要传递一个 executor
执行器,执行器马上执行executor
接受两个参数,分别是 resolve
和 reject
promise
只能从 pending
到 rejected
, 或者从 pending
到 fulfilled
,状态一旦确认,就不会再改变promise
都有 then
方法,then
接收两个参数,分别是 promise
成功的回调 onFulfilled
, 和 promise
失败的回调 onRejected
then
时,promise
已经成功,则执行 onFulfilled
,并将 promise
的值做为参数传递进去。 若是 promise
已经失败,那么执行 onRejected
, 并将 promise
失败的缘由做为参数传递进去。若是 promise
的状态是 pending
,须要将 onFulfilled
和 onRejected
函数存放起来,等待状态肯定后,再依次将对应的函数执行。then
的参数 onFulfilled
和 onRejected
能够缺省promise
能够 then
屡次,promise
的 then
方法返回一个新的 promise
then
返回的是一个结果,那么就会把这个结果做为参数,传递给下一个 then
的成功的回调 onFulfilled
then
中抛出了异常,那么就会把这个异常做为参数,传递给下一个 then
的失败的回调 onRejected
then
返回的是一个 promise
, 那么须要等这个 promise
,那么会等这个 promise
执行完。promise
若是成功, 就走下一个 then
的成功,若是失败,就走下一个 then
的失败// 定义三种状态的常量 const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; // pomise 接收一个 executor 执行器,执行器马上执行 function MyPromise(executor) { const _this = this; // promise 当前的状态 _this.currentState = PENDING; _this.value = undefined; // 保存 then 中的回调,只有当 promise 状态为 pending 时才会缓存,而且每一个实例至多缓存一个 _this.onFulfilledCallbacks = []; _this.onRejectedCallbacks = []; function resolve(value) { if (value instanceof MyPromise) { // 若是 value 是个 Promise,调用 then 方法继续执行 value.then(resolve, reject); } // 异步执行,保证执行顺序 setTimeout(() => { if (_this.currentState === PENDING) { _this.currentState = FULFILLED; _this.value = value; _this.onFulfilledCallbacks.forEach(fn => fn()); } }); } function reject(reason) { // 异步执行,保证执行顺序 setTimeout(() => { if (_this.currentState === PENDING) { _this.currentState = REJECTED; _this.value = reason; _this.onRejectedCallbacks.forEach(fn => fn()); } }); } try { executor(resolve, reject); } catch(err) { reject(err); } } MyPromise.prototype.constructor = MyPromise; MyPromise.prototype.then = function (onFulfilled, onRejected) { const _this = this; // 2.2.1 onFulfilled 和 onRejected 都是可选参数 // 2.2.5 onFulfilled 和 onRejected 必须做为函数被调用 // 2.2.7.3 若是 onFulfilled 不是一个函数,promise2 以promise1的值fulfilled // 2.2.7.4 若是 onRejected 不是一个函数,promise2 以promise1的reason rejected onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : valve => valve; onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }; // 2.2.7,then 必须返回一个新的 promise const promise2 = new MyPromise((resolve, reject) => { if (_this.currentState === FULFILLED) { // 2.2.4 保证 onFulfilled,onRjected 异步执行 setTimeout(() => { try { // 2.2.7.1 onFulfilled 或 onRejected 执行的结果为 x, 调用 resolutionProcedure const x = onFulfilled(_this.value); resolutionProcedure(promise2, x, resolve, reject); } catch(err) { // 2.2.7.2 若是 onFulfilled 或者 onRejected 执行时抛出异常 err, promise2 须要被 reject reject(err); } }); } if (_this.currentState === REJECTED) { setTimeout(() => { try { // 2.2.7.1 onFulfilled 或 onRejected 执行的结果为 x, 调用 resolutionProcedure const x = onRejected(_this.value); resolutionProcedure(promise2, x, resolve, reject); } catch(err) { // 2.2.7.2 若是 onFulfilled 或者 onRejected 执行时抛出异常 err, promise2 须要被 reject reject(err); } }); } if (_this.currentState === PENDING) { _this.onFulfilledCallbacks.push(() => { setTimeout(() => { try { const x = onFulfilled(_this.value); resolutionProcedure(promise2, x, resolve, reject); } catch(err) { reject(err); } }); }); _this.onRejectedCallbacks.push(() => { setTimeout(() => { try { const x = onRejected(_this.value); resolutionProcedure(promise2, x, resolve, reject); } catch(err) { reject(err); } }); }); } }); return promise2; } // 2.3 resolutionProcedure(promise2, x, resolve, reject) function resolutionProcedure(promise2, x, resolve, reject) { // 2.3.1 若是 promise2 和 x 相等,那么 reject promise with a TypeError if (promise2 === x) { reject(new TypeError('Error')); } // 2.3.2 若是 x 为 Promise,状态为 pending 须要继续等待不然执行 if (x instanceof MyPromise) { if (x.currentState === PENDING) { x.then(value => { resolutionProcedure(promise2, value, resolve, reject); }, reject) } else { x.then(resolve, reject); } } // 2.3.3.3.3 reject 或者 resolve 其中一个执行过的话,忽略其余的 let called = false; // 2.3.3,判断 x 是否为对象或者函数 if ( x && (typeof x === 'object' || typeof x === 'function') ) { try { let then = x.then; // 2.3.3.3 若是 then 是一个函数,then.call(x, resolvePromise, rejectPromise) if (typeof then === 'function') { then.call( x, y => { if (called) return; called = true; // 2.3.3.3.1 resolvePromiseFn 的 入参是 y, 执行 resolutionProcedure(promise2, y, resolve, reject); resolutionProcedure(promise2, y, resolve, reject); }, r => { if (called) return; called = true; // 2.3.3.3.2 rejectPromise 的 入参是 r, reject promise with r. reject(r); } ); } else { // 2.3.3.4 若是 then 不是一个 function. fulfill promise with x. resolve(x); } } catch(err) { // 2.3.3.2 若是 x.then 这步出错,那么 reject promise with err as the reason if (called) return; called = true; reject(err) } } else { // 2.3.4 若是 x 不是一个 object 或者 function,fulfill promise with x. resolve(x); } } module.exports = MyPromise;
promises-aplus-tests
这个 npm
包能够帮助咱们测试所编写的 promise
代码是否符合 Promises/A+
的规范。微信
不过咱们须要先增长如下代码去提供测试的接口异步
MyPromise.defer = MyPromise.deferred = function () { let dfd = {}; dfd.promise = new MyPromise((resolve, reject) => { dfd.resolve = resolve; dfd.reject = reject; }); return dfd; }
经过 npm
安装async
npm install promises-aplus-tests --save-dev
经过执行 promises-aplus-tests target.js
能够执行测试,promises-aplus-tests
中共有 872 条测试用例。
能够在个人 github 进行查看,经过 npm run test
来执行测试,能够经过全部的测试用例。
更多精彩内容,欢迎关注微信公众号~