实现一个符合Promise/A+规范的Promise
栏目: JavaScript · 发布时间: 5年前
内容简介:所谓在实现一个能通过对照
所谓 Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说, Promise
是一个对象,从它可以获取异步操作的消息。 Promise
提供统一的 API,各种异步操作都可以用同样的方法进行处理。
简易版Promise的实现
在实现一个能通过 Promise A+
规范测试的 Promise
版本之前,我们先写一个简易版的 Promise
,结合我们平常使用 Promise
的姿势:
const promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } });
对照 Promise A+规范 开始撸代码。
1.1 “promise” is an object or function with a then method whose behavior conforms to this specification. 1.2 “thenable” is an object or function that defines a then method. 1.3 “value” is any legal JavaScript value (including undefined, a thenable, or a promise). 1.4 “exception” is a value that is thrown using the throw statement. 1.5 “reason” is a value that indicates why a promise was rejected.
先实现一个 Promise
的构造函数:
function Promise(executor) { let self = this self.status = 'pending' // 一个promise必须处于三种状态之一: 请求态(pending), 完成态(fulfilled),拒绝态(rejected) self.value = null self.onResolveCallbacks = [] // Promise resolve的时候可能会有多个回调 self.onRejectCallbacks = [] // 同上 executor(resolve, reject) // 执行executor }
- 在构造函数内部先使用一个常量
self
,当异步执行的时候可以获取正确的this
对象 -
Promise
的初始状态应该是pending
-
value
变量用于存储resolve
或者reject
的值,这里其实同时表示了规范中的value/reason
- 当执行完
Promise
之后当前Promise
的status
可能还是pending
,这个时候要把then
方法中传入的回调存储到对应的callBackList
我们知道 Promise
接受一个函数作为参数,这个函数有 resolve
和 reject
两个函数作为参数, resolve
函数可以将 Promise
的状态从 pending
转为 fulfilled
,并将异步操作的结果作为参数传递出去, reject
函数可以将 Promise
的状态从 pending
转为 rejected
,并将异步操作失败报出的错误作为参数传递出去,下面我们来实现这两个函数:
function Promise(executor) { let self = this self.status = 'pending' self.value = null self.onResolveCallbacks = [] self.onRejectCallbacks = [] function resolve(value) { if (self.status === 'pending') { self.status = 'fulfilled' self.value = value self.onResolveCallbacks.forEach(cb => cb(self.value)) } } function reject(reason) { if (self.status === 'pending') { self.status = 'rejected' self.value = reason self.onRejectCallbacks.forEach(cb => cb(self.value)) } } try { executor(resolve, reject) } catch (e) { reject(e) } }
这里有一点需要解释的是,执行 executor
有可能会出错就像下面这样,如果出错了 Promise
应该 reject
这个出错的值:
new Promise(function(resolve, reject) { throw 2 })
下一步,实现逻辑较为复杂的 then
函数,也是整个 Promise
的精髓所在:
A promise must provide a then method to access its current or eventual value or reason. A promise’s then method accepts two arguments: promise.then(onFulfilled, onRejected)
因为 then
方法可以链式调用,所以我们把它构造在 Promise
的原型链上:
Promise.prototype.then = function(onFulfilled, onRejected) { // 内部实现 }
根据标准 2.2.1
和 2.2.7
我们可以写出如下代码:
Promise.prototype.then = function(onFulfilled, onRejected) { let self = this onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v // 2.2.1.1 onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r } // 2.2.1.2 if (self.status === 'pending') { self.onResolveCallbacks.push(onFulfilled) self.onRejectCallbacks.push(onRejected) } else if (self.status === 'fulfilled') { onFulfilled(self.value) } else if (self.status === 'rejected') { onRejected(self.value) } }
为了透传我们在 onFulfilled/onRejected
不是一个函数的时候也创造了一个函数返回当前 onFulfilled/onRejected
的值,当 status
处于 pending
状态,我们不能确定调用 onFulfilled
还是 onRejected
,只有等状态确定后才可以处理,所以这里把两个 callback
存入回调数组里面,如果是确定状态则调用对应的方法即可,这就是最简易版本的 Promise
,来试一下:
new Promise(function(resolve, reject) { setTimeout(() => { resolve(1) }) }).then((value) => { console.log(value) // 打印结果 1 })
实现一个符合Promise/A+规范的Promise
以上面的简易版为基础,继续改造 then
方法,规范中有提到说 then
需要返回一个 Promise
,这也是它可以链式操作的关键所在:
then must return a promise [3.3]. promise2 = promise1.then(onFulfilled, onRejected);
虽然说规范里面允许我们返回同一个 Promise
,但是我们这里遵循大多数 Promise
的实现给它返回一个新的 Promise
:
Promise.prototype.then = function(onFulfilled, onRejected) { let self = this onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v // 2.2.1.1 onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r } // 2.2.1.2 let returnedPromise = null // 2.2.7 if (self.status === 'pending') { return returnedPromise = new Promise((resolve, reject) => { self.onResolveCallbacks.push(onFulfilled) self.onRejectCallbacks.push(onRejected) }) } else if (self.status === 'fulfilled') { return returnedPromise = new Promise((resolve, reject) => { onFulfilled(self.value) }) } else if (self.status === 'rejected') { return returnedPromise = new Promise((resolve, reject) => { onRejected(self.value) }) } }
根据规范的定义,我们这里还需要定义一个 [[Resolve]](promise2, x)
解析函数,解析 onFulfilled
或 onRejected
的返回值,同时对两个方法执行期间抛出的错误进行 reject
:
Promise.prototype.then = function(onFulfilled, onRejected) { let self = this onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v // 2.2.1.1 onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r } // 2.2.1.2 let returnedPromise = null // 2.2.7 if (self.status === 'pending') { return returnedPromise = new Promise((resolve, reject) => { self.onResolveCallbacks.push(() => { try { let x = onFulfilled(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }) self.onRejectCallbacks.push(() => { try { let x = onRejected(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }) }) } else if (self.status === 'fulfilled') { return returnedPromise = new Promise((resolve, reject) => { try { let x = onFulfilled(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }) } else if (self.status === 'rejected') { return returnedPromise = new Promise((resolve, reject) => { try { let x = onRejected(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }) } } function ResolutionProcedure(promise, x, resolvePromise, rejectPromise) { // 内部实现 }
接下来只需要关注这个 ResolutionProcedure
函数的内部实现,这里其实规范都给出了所有的详细步骤,按照规范来做就行:
function ResolutionProcedure(promise, x, resolvePromise, rejectPromise) { try { if (promise === x) { return rejectPromise(new TypeError('2.3.1')) } if (x instanceof Promise) //2.3.2 x.then(((value)=>{ ResolutionProcedure(promise, value, reslove, reject); },(reason)=>{ reject(reason) })); let called = false // 2.3.3.3.3 if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // 2.3.3 try { // 2.3.3.1 let then = x.then if (typeof then === 'function') { // 2.3.3.3 then.call(x, (value) => { if (called) return called = true return ResolutionProcedure(promise, value, resolvePromise, rejectPromise) }, (reason) => { if (called) return called = true return rejectPromise(reason) }) } else { return resolvePromise(x) // 2.3.4 } } catch (e) { if (called) return called = true return rejectPromise(e) // 2.3.3.2 } } else { return resolvePromise(x) // 2.3.3.4 } } catch (e) { return rejectPromise(e) } }
然后我们注意到规范 2.2.4
中说了只有在执行栈包含平台代码的时候才可以调用 onFulfilled
和 onRejected
:
onFulfilled or onRejected must not be called until the execution context stack contains only platform code.
解释文案中也说了其实就是异步执行,我们可以用 setTimeout
或 setImmediate
来做到这一点:
Promise.prototype.then = function(onFulfilled, onRejected) { let self = this onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v // 2.2.1.1 onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r } // 2.2.1.2 let returnedPromise = null // 2.2.7 if (self.status === 'pending') { return returnedPromise = new Promise((resolve, reject) => { self.onResolveCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) self.onRejectCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) }) } else if (self.status === 'fulfilled') { return returnedPromise = new Promise((resolve, reject) => { setTimeout(() => { try { let x = onFulfilled(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) } else if (self.status === 'rejected') { return returnedPromise = new Promise((resolve, reject) => { setTimeout(() => { try { let x = onRejected(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) } }
测试
这里用官方提供的 promises-aplus-tests
来验证我们写出来的库是否符合规范,为了跑测试我们还需要加上 deferred
方法:
Promise.deferred = function() { let defer = {} defer.promise = new Promise((resolve, reject) => { defer.resolve = resolve defer.reject = reject }) return defer } module.exports = Promise
跑一下测试:
npm install -g promises-aplus-tests promises-aplus-tests ./promise.js
说明我们写的库是符合的 Promise/A+
标准的
扩展
Promise/A+
规范只是定义了 then
方法,但是 Promise
本身还有一些其他的方法,我们也可以实现一下:
- catch
Promise.prototype.catch = function(onRejected) { return this.then(null, onRejected) }
- race
Promise.prototype.race = function(values) { return new Promise(function(resolve, reject) { for (let i = 0, len = values.length; i < len; i++) { values[i].then(resolve, reject) } }) }
- all
Promise.all = function(promises) { return new Promise(function(resolve, reject) { let resolvedCounter = 0 let promiseNum = promises.length let resolvedValues = new Array(promiseNum) for (let i = 0; i < promiseNum; i++) { (function(i) { Promise.resolve(promises[i]).then(function(value) { resolvedCounter++ resolvedValues[i] = value if (resolvedCounter === promiseNum) { return resolve(resolvedValues) } }, function(reason) { return reject(reason) }) })(i) } }) }
- finally
Promise.prototype.finally = function(callback) { let constructor = this.constructor return this.then( function(value) { return constructor.resolve(callback()).then(function() { return value }) }, function(reason) { return constructor.resolve(callback()).then(function() { return constructor.reject(reason) }) } ) }
最后奉上实现后的全部代码:
function Promise(executor) { let self = this self.status = 'pending' self.value = null self.onResolveCallbacks = [] self.onRejectCallbacks = [] function resolve(value) { if (self.status === 'pending') { self.status = 'fulfilled' self.value = value self.onResolveCallbacks.forEach(cb => cb(self.value)) } } function reject(reason) { if (self.status === 'pending') { self.status = 'rejected' self.value = reason self.onRejectCallbacks.forEach(cb => cb(self.value)) } } try { executor(resolve, reject) } catch (e) { reject(e) } } Promise.prototype.then = function(onFulfilled, onRejected) { let self = this onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v // 2.2.1.1 onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r } // 2.2.1.2 let returnedPromise = null // 2.2.7 if (self.status === 'pending') { return returnedPromise = new Promise((resolve, reject) => { self.onResolveCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) self.onRejectCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) }) } else if (self.status === 'fulfilled') { return returnedPromise = new Promise((resolve, reject) => { setTimeout(() => { try { let x = onFulfilled(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) } else if (self.status === 'rejected') { return returnedPromise = new Promise((resolve, reject) => { setTimeout(() => { try { let x = onRejected(self.value) ResolutionProcedure(returnedPromise, x, resolve, reject) } catch (e) { reject(e) } }, 0) }) } } Promise.prototype.catch = function(onRejected) { return this.then(null, onRejected) } Promise.prototype.race = function(values) { return new Promise(function(resolve, reject) { for (let i = 0, len = values.length; i < len; i++) { values[i].then(resolve, reject) } }) } Promise.all = function(promises) { return new Promise(function(resolve, reject) { let resolvedCounter = 0 let promiseNum = promises.length let resolvedValues = new Array(promiseNum) for (let i = 0; i < promiseNum; i++) { (function(i) { Promise.resolve(promises[i]).then(function(value) { resolvedCounter++ resolvedValues[i] = value if (resolvedCounter === promiseNum) { return resolve(resolvedValues) } }, function(reason) { return reject(reason) }) })(i) } }) } Promise.prototype.finally = function(callback) { let constructor = this.constructor return this.then( function(value) { return constructor.resolve(callback()).then(function() { return value }) }, function(reason) { return constructor.resolve(callback()).then(function() { return constructor.reject(reason) }) } ) } function ResolutionProcedure(promise, x, resolvePromise, rejectPromise) { try { if (promise === x) { return rejectPromise(new TypeError('2.3.1')) } if (x instanceof Promise) //2.3.2 x.then(((value) => { ResolutionProcedure(promise, value, reslove, reject) }, (reason) => { reject(reason) })) let called = false // 2.3.3.3.3 if (x !== null && (typeof x === 'object' || typeof x === 'function')) { // 2.3.3 try { // 2.3.3.1 let then = x.then if (typeof then === 'function') { // 2.3.3.3 then.call(x, (value) => { if (called) return called = true return ResolutionProcedure(promise, value, resolvePromise, rejectPromise) }, (reason) => { if (called) return called = true return rejectPromise(reason) }) } else { return resolvePromise(x) // 2.3.4 } } catch (e) { if (called) return called = true return rejectPromise(e) // 2.3.3.2 } } else { return resolvePromise(x) // 2.3.3.4 } } catch (e) { return rejectPromise(e) } } Promise.deferred = function() { let defer = {} defer.promise = new Promise((resolve, reject) => { defer.resolve = resolve defer.reject = reject }) return defer } module.exports = Promise
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 如何判断URL格式是否符合规范?
- [译] 打造符合用户期望的应用质量
- 符合语言习惯的 Python 优雅编程技巧
- Web框架的MVC符合标准的MVC吗?
- 只会用就out了,手写一个符合规范的Promise
- 手写一款符合Promise/A+规范的Promise
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
微信营销与运营一册通
何秀芳、葛存山 / 人民邮电出版社 / 2014-10
《微信营销与运营一册通》深入介绍了当今最为火热的话题——微信营销,内容全面、系统和深入。它基于微信的最新版本,从策略、技巧与案例等多角度详细解析了微信的营销与运营,所有内容都是行业经验的结晶,旨在为企业或个人运用微信提供有价值的参考。《微信营销与运营一册通》主要内容如下。 * 5大微信营销利器:书中介绍了5大微信营销利器,包括漂流瓶、二维码、LBS功能、朋友圈和公众平台等。 * 6大微......一起来看看 《微信营销与运营一册通》 这本书的介绍吧!