promise 标准
在实现 Promise 之前要清楚的是 JavaScript 中的 Promise 遵循了 Promises/A+ 规范,所以我们在编写 Promise 时也应当遵循这个规范,建议认真、仔细读几遍这个规范。最好是理解事件循环,这样对于理解js中的异步是怎么回事非常重要。
基本使用
new Promise( function(resolve, reject) {...} /* executor */ );
new Promise((resolve, reject)=> { AjaxRequest.post({ url: 'url', data: {}, sueccess: ()=> { resolve(res) }, fail: (err)=> { reject(err) } }) }).then((res)=> { // do some }).then(value => { }).catch((err)=> { // do some })
promise 是处理异步结果的一个对象,承若状态改变时调用对应的回调函数,resolve、reject用来改变promise 的状态,then 绑定成功、失败的回调。
环境准备
安装测试工具以及nodemon因为我们要在node环境调试自己写的promise
// nodemon npm install nodemon -D // promise 测试工具 npm install promises-aplus-tests -D
增加脚本命令
"testPromise": "promises-aplus-tests myPromise/promise3.js",
"dev": "nodemon ./myPromise/index.js -i "
各自的路径改成自己的即可,这个在后面会用来测试。
基本架子
根据规范实现一个简单的promise,功能如下
- promise的三种状态(PENDING、FULFILLED、REJECTED)
- 状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态
- 绑定then的回调
- 返回成功、失败的值
- 一个promise 支持调用多次then
- 支持捕获异常
/* 基本架子 根据promise A+ 规范还要处理then链式调用以及返回值传递的问题,后续在promise2、promise3 处理 */ const PENDING = 'PENDING', FULFILLED = 'FULFILLED', REJECTED = 'REJECTED'; class myPromise { constructor (executor) { this.status = PENDING this.value = undefined this.reason = undefined
this.onResolveCallbacks = [] this.onRejectedCallbacks = [] const resolve = (value) => { if (this.status === PENDING) { this.status = FULFILLED this.value = value // 发布 this.onResolveCallbacks.forEach(fn => fn()) } } const reject = (reason) => { if (this.status === PENDING) { this.status = REJECTED this.reason = reason // 发布 this.onRejectedCallbacks.forEach(fn => fn()) } } try { // 执行传进来的fn, 在给他提供改变状态的fn executor(resolve, reject) } catch(e) { reject(e) } } // 订阅回调函数 then (onFulfilled, onRejected) { if (this.status = PENDING) { // 订阅 this.onResolveCallbacks.push(() => { onFulfilled(this.value) }) this.onRejectedCallbacks.push(() => { onRejected(this.reason) }) } if (this.status === FULFILLED) { onFulfilled(this.value) } if (this.status === REJECTED) { onRejected(this.reason) } } } module.exports = myPromise
订阅
传进来的fn是一个执行器,接受resolve、reject参数,通常我们在构造函数中需要调用某个接口,这是一个异步的操作,执行完构造函数之后,在执行then(),这个时候的状态还是pending,所以我们需要把then 绑定的回调存起来,也可以理解为promise对象订阅了这个回调。
发布
在 resolve,reject函数中中我们改变了promise 对象的状态,既然状态改变了,那么我们需要执行之前订阅的回调,所以在不同的状态下执行对应的回调即可。
流程
如上所示,实例化对象,执行构造函数,碰到异步,挂起,然后执行then()方法,绑定了resolve、reject的回调。如果异步有了结果执行对应的业务逻辑,调用resolve、或者reject,改变对应的状态,触发我们绑定的回调。
以上就是最基本的promise架子,但是还有promise 调用链没有处理,下面继续完善...
完善promise 调用链
promose 的精妙的地方就是这个调用链,首先then 函数会返回一个新的promise 对象,并且每一个promise 对象又有一个then 函数。惊不惊喜原理就是那么简单,回顾下then的一些特点
then 特点
- then 返回一个新的promise 对象
- then 绑定的回调函数在异步队列中执行(evnet loop 事件循环)
- 通过return 来传递结果,跟fn一样如果没有return,默认会是 underfined
- 抛出异常执行绑定的失败函数(最近的promise),如果没有,则执行catch
- then中不管是不是异步只要resolve、rejected 就会执行对应 onFulfilled、onRejected 函数
- then中返回promise状态跟执行回调的结果有关,如果没有异常则是FULFILLED,就算没有retun 也是FULFILLED,值是underfined,有异常就是REJECTED,接着走下个then 绑定的onFulfilled 、onRejected 函数
根据上面的特点以及阅读规范我们知道then()函数主要需要处理以下几点
- 返回一个新的promise
- 值怎么传给then返回的那个promise
- 状态的改变
返回一个新的promise
因为promise 的链式调用涉及到状态,所以then 中返回的promise 是一个新的promise
then(onFulfilled, onRejected) { let promise2 = new Promise((resolve, reject) => { // do ... }) return promise2 }
值的传递、状态的改变
let p = new myPromise((resolve, rejected) => { // do ... }) p.then( value => { return 1 }, reason => {} ) .then( value => { return new Promise((resolve, rejected) => { resolve('joel') }) }, reason => {} ) .then( value => { throw 'err: 出错啦' }, reason => {} )
then 返回的值可能是一个普通值、promise对象、function、error 等对于这部分规范文档也有详细的说明
[[Resolve]](promise, x)
这个可以理解为promise 处理的过程,其中x是执行回调的一个值,promise 是返回新的promise对象,完整代码如下
我们将这部分逻辑抽成一个独立的函数 如下
// 处理then返回结果的流程 function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise #<myPromise>')) } let called = false if ((typeof x === 'object' && x !== null) || typeof x === 'function') { try { let then = x.then // 判断是否是promise if (typeof then === 'function') { then.call(x, (y) => { // 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y) if (called) return called = true resolvePromise(promise2, y, resolve, reject) }, (r) => { if (called) return called = true reject(r) }) } else { resolve(x) } } catch (e) { if (called) return called = true reject(e) } } else { // 如果 x 不为对象或者函数,以 x 普通值执行回调 resolve(x) } }
测试
promises-aplus-tests 这个工具我们必须实现一个静态方法deferred,官方对这个方法的定义如下:
deferred: 返回一个包含{ promise, resolve, reject }的对象
promise 是一个处于pending状态的promise
resolve(value) 用value解决上面那个promise
reject(reason) 用reason拒绝上面那个promise
添加如下代码
myPromise.defer = myPromise.deferred = function () { let deferred = {} deferred.promise = new myPromise((resolve, reject) => { deferred.resolve = resolve deferred.reject = reject }) return deferred }
在编辑执行我们前面加的命令即可
npm run testMyPromise
完善其他方法
- all
- allSettled
- any
- race
- catch
- finlly
npm run dev // 可以用来测试这些方法
源码
比较官方的源码: https://github.com/then/promise
参考
https://www.jianshu.com/p/4d266538f364
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all