零、原文与资料
1. 手写 Promise;
一、Promise/A+ 规范
1.Promise存在三个状态:pending(等待态)、fulfilled(成功态)、rejected(失败态);
2.pending为初始态,并可以转化为fulfilled和rejected;
3.成功时,不可转为其他状态,且必须有一个不可改变的值 (value);
4.失败时,不可转为其他状态,且必须有一个不可改变的原因 (reason);
5.new Promise(executor = (resolve, reject) => {resolve(value)}), resolve(value)将状态置为 fulfilled;
6.new Promise(executor = (resolve, reject) => {reject(reason)}), reject(reason)将状态置为 rejected;
7.如果 executor 执行异常也会 reject();
8.thenable: then(onFulfilled, onRejected?);
8.1 onFulfilled: status 为 fulfilled,执行 onFulfilled, 传入 value
8.2 onRejected: status 为 rejected, 执行 onRejected
二、同步 Promise
同步 Promise 没啥需要特别注意的地方,代码如下:
// 1.Promise存在三个状态:pending(等待态)、fulfilled(成功态)、rejected(失败态) const STATUS_PENDING = 'pending' const STATUS_FULFILLED = 'fulfilled' const STATUS_REJECTED = 'rejected' class myPromise { constructor(executor) { // pending为初始态,并可以转化为fulfilled和rejected this.status = STATUS_PENDING this.value = '' // 3 this.reason = '' // 4 let resolve = value => { // 5. if (this.status === STATUS_PENDING) { this.status = STATUS_FULFILLED this.value = value } } let reject = reason => { //6. if (this.status === STATUS_PENDING) { this.status = STATUS_REJECTED this.reason = reason } } // 7. try { executor(resolve, reject); } catch (err) { reject(err); } } // 8. then(onFulfilled = () => {}, onRejected = () => {}) { // 8.1 if (this.status === STATUS_FULFILLED) { onFulfilled(this.value) } // 8.2 if (this.status === STATUS_REJECTED) { onRejected(this.reason) } } } let ps = new myPromise(resolve => { console.log('before resolve') resolve(1) }) ps.then(res => { console.log(res) }) let pe = new myPromise((resolve, reject) => { console.log('before reject') reject('reject error') }) pe.then(res => { console.log(res) }, error => { console.log(error) })
在上面的两个例子中,所有任务的执行都是同步的,可以与接下来的异步 promise 的执行顺序对比下。
三、异步 Promise
先放下我们的运行实例:
let pa = new myPromise(resolve => { console.log('before resolve') setTimeout(()=>{ resolve(1) },1000) }) pa.then(res => { console.log(res) })
在这里,我们的 executor 函数体中有异步的代码块,那么在执行 executor(resolve, reject); 的时候,setTimeout中的任务就会被推入异步执行栈中,等待主线程中的宏任务(详见 EventLoop in Js)全部计算完成再执行这个任务,因此,我们打断点会发现, then() 的执行会早于 resolve(1) , 而在 then() 执行的时候,pa.status 依然是 pending,接下啦根据逻辑判断 then 函数执行完成退出,然后执行异步任务,整个代码执行完毕,故控制台只会打印出 'before resolve'。
明白了这里的执行顺序,我们即可以进行完善,代码如下:(标记 改 A)
const STATUE_PENDING = 'pending'; const STATUE_FULFILLED = 'fulfilled'; const STATUS_REJECTED = 'rejected'; class MyPromise { constructor(executor) { // pending 是初始态,并可以转化成 fulfilled 和 rejected this.status = STATUE_PENDING; this.value = ''; // 3. this.reason = ''; // 4. // 改 A start // 存放成功的数组 this.onResolvedCallbacks = []; // 存放失败的数组 this.onRejectedCallbacks = []; // 改 A end let resolve = value => { // 5. if (this.status === STATUE_PENDING) { this.status = STATUE_FULFILLED; this.value = value; // 改 A start // 成功之后的执行栈, this.onResolvedCallbacks.forEach(fn => fn()); // 改 A end } } let reject = reason => { // 6. if (this.status === STATUE_PENDING) { this.status = STATUS_REJECTED; this.reason = reason; // 改 A start // 成功之后的执行栈, this.onRejectedCallbacks.forEach(fn => fn()); // 改 A end } } // 7. try { executor(resolve, reject); } catch (error) { reject(err); } } // 8 then(onFulfilled = () => {}, onRejected = () => {}){ // 8.1 if (this.status === STATUE_FULFILLED) onFulfilled(this.value); // 8.2 if (this.status === STATUS_REJECTED) onRejected(this.reason); // 改 A start // 如果是处于异步任务的 if (this.status === STATUE_PENDING) { // 推入相应的执行栈 this.onResolvedCallbacks.push(() => onFulfilled(this.value)); this.onRejectedCallbacks.push(() => onRejected(this.reason)); } // 改 A end } }
为什么要分析下这边的执行顺序,一来复习下 EventLoop,二来对下面的链式调用的理解比较重要(这里也是打断点才发现的,推翻了一直以来对 Promise.then 的理解,之前一直认为 executor 中的异步任务执行完了才真正的去执行 then 函数和里面的onFulfilled/onRejected 函数)。
四、new Promise().then().then()... 链式调用
Promise 的链式调用和 jquery 的链式调用是不同的,在 jquery 或者一些其他的三方包中,我们在函数末尾加上 return this 即可实现,所以这里无论链多少,都是在同一个对象上做文章。而 Promise 的每一次(链式)调用,其都会产生一个新的 Promise 对象, 并基于这个新的 Promise 调用 then 函数,虽然我们写的时候是.then().then()...
。 首先我们来看实例:
let pc = new MyPromise((resolve, reject) => { console.log(0); setTimeout(() => { resolve(1); }, 3000); }) pc.then(res => { console.log(res); return new MyPromise(resolve => { console.log(2); setTimeout(() => { resolve(3) }, 3000); }) }).then(res => { console.log(res); })
注意下结构,我们在第一个 then 的参数函数中会有一个新的 Promise 返回。
然后是 MyPromise 类:
const STATUE_PENDING = 'pending'; const STATUE_FULFILLED = 'fulfilled'; const STATUS_REJECTED = 'rejected'; function resolvePromise(promise2, x, resolve, reject) { // 处理循环引用报错 if (x === promise2) { // reject 报错 return reject(new TypeError('chaining cycle detected for promise')); } // 记录, 防止多次调用 let called; // x 是对象(不包括 null)或者函数 if (x != null && (typeof x === 'object' || typeof x === 'function')) { try { // A+ 规定,声明 then = x 的 then 方法 let then = x.then; // then 是 function,则默认是 promise if (typeof then === 'function') { // 就让 then 执行, 第一个参数是 this, 后面是成功的回调和失败的回调 then.call(x, y => { // 成功和失败只能调用一个 if (called) return; called = true; // resolve 的结果依旧是promise 那就继续解析 resolvePromise(promise2, y, resolve, reject); }, err => { // 成功和失败只能调用一个 if (called) return; called = true; // 失败,停止继续调用 reject(err); }) } else {// 不是的话直接 resolve 即可 resolve(x); } } catch (error) { // 出错,即失败 if (called) return; called = true; // 取 then 出错了那就不继续了 reject(error); } } else { resolve(x); } } class MyPromise { constructor(executor) { this.status = STATUE_PENDING; this.value = ''; this.reason = ''; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; let resolve = value => { if (this.status === STATUE_PENDING) { this.status = STATUE_FULFILLED; this.value = value; this.onResolvedCallbacks.forEach(fn => fn()); } } let reject = reason => { if (this.status === STATUE_PENDING) { this.status = STATUS_REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn()); } } try { executor(resolve, reject); } catch (error) { reject(err); } } then(onFulfilled, onRejected){ // onFulfilled 不是函数, 则忽略,直接返回 value onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; // onRejected 不是函数, 则忽略,直接扔出错误 onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; }; let promise2 = new MyPromise((resolve, reject) => { if (this.status === STATUE_FULFILLED) { // 推一个异步任务 setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); } if (this.status === STATUS_REJECTED) { // 推一个异步任务 setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); } if (this.status === STATUE_PENDING) { // 推到执行栈中 this.onResolvedCallbacks.push(() => { setTimeout(() => { try { let x = onFulfilled(this.value); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); }); this.onRejectedCallbacks.push(() => { setTimeout(() => { try { let x = onRejected(this.reason); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); }) } }); return promise2; } }
这里要注意的是 then 函数的执行会产生一个新的 Promise, 第一个 then 函数的参数函数的执行也会产生一个新的 Promise。
五、其他:catch、resolve、reject、race和all
这里除 catch 外其余均是静态方法:
1. catch(特殊的 then 方法):
catch(fn){ return this.then(null,fn) }
2.reslove(resolve 一个值)
MyPromise.resolve = val => new Promise(resolve=> resolve(val))
3.reject(reject 一个值)
MyPromise.reject = val => new Promise((resolve,reject)=> reject(val))
4.race Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
MyPromise.race = promises => { return new MyPromise((resolve, reject) => promises.forEach(pro => pro.then(resolve, reject)) ) }
5.all Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
MyPromise.all = function (promises) { return new Promise((resolve, reject) => { let index = 0; let result = []; if (promises.length === 0) { resolve(result); } else { function processValue(i, data) { result[i] = data; if (++index === promises.length) { resolve(result); } } for (let i = 0; i < promises.length; i++) { //promises[i] 可能是普通值 Promise.resolve(promises[i]).then((data) => { processValue(i, data); }, (err) => { reject(err); return; }); } } }); }
六、一点感悟
Promise/A+ 规范还是值得每一个合格的前端开发去阅读的。
得:再一次复习了 eventLoop,推翻了之前对 then 函数的执行时机的错误认识;
目标:完全理清楚链式调用的任务栈和执行顺序以及 Promise.all 的原理;