ES6 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | // 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
先放下我们的运行实例:
1 2 3 4 5 6 7 8 9 | 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)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | 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 或者一些其他的三方包中,我们在函数末尾加上 returnthis 即可实现,所以这里无论链多少,都是在同一个对象上做文章。而 Promise 的每一次(链式)调用,其都会产生一个新的 Promise 对象, 并基于这个新的 Promise 调用 then 函数,虽然我们写的时候是.then().then()...
。 首先我们来看实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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 类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 | 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 方法):
1 2 3 | catch (fn){ return this .then( null ,fn) } |
2.reslove(resolve 一个值)
1 | MyPromise.resolve = val => new Promise(resolve=> resolve(val)) |
3.reject(reject 一个值)
1 | MyPromise.reject = val => new Promise((resolve,reject)=> reject(val)) |
4.race Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。
1 2 3 4 5 | MyPromise.race = promises => { return new MyPromise((resolve, reject) => promises.forEach(pro => pro.then(resolve, reject)) ) } |
5.all Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | 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 ; }); } } }); } |