本文最后更新于:2023年3月19日 晚上
本文转自:https://juejin.im/post/6860037916622913550#heading-10
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 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
| const PENDING = "PENDING"; const FULFILLED = "FULFILLED"; const REJECTED = "REJECTED";
class myPromise { constructor(executor) { this.status = PENDING; this.value = null; this.reason = null; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = [];
const resolve = (value) => { console.log(value); if (this.status === PENDING) { this.status = FULFILLED; this.value = value; this.onFulfilledCallbacks.forEach((fn) => fn(this.value)); } }; const reject = (reason) => { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn(this.reason)); } }; try { executor(resolve, reject); } catch (e) { reject(e); } }
then(onFulfilled, onRejected) { if (typeof onFulfilled !== "function") onFulfilled = (value) => value; if (typeof onRejected !== "function") onRejected = (reason) => { throw reason; }; const self = this; return new myPromise((resolve, reject) => { if (self.status === PENDING) { self.onFulfilledCallbacks.push(() => { try { setTimeout(() => { const res = onFulfilled(self.value); if (res instanceof myPromise) { res.then(resolve, reject); } else { resolve(res); } }); } catch (error) { reject(error); } }); self.onRejectedCallbacks.push(() => { try { setTimeout(() => { const res = onRejected(self.value); if (res instanceof myPromise) { res.then(resolve, reject); } else { reject(res); } }); } catch (error) { reject(error); } }); } else if (self.status === FULFILLED) { try { setTimeout(() => { const res = onFulfilled(self.value); if (res instanceof myPromise) { res.then(resolve, reject); } else { resolve(res); } }); } catch (error) { reject(error); } } else if (self.status === REJECTED) { try { setTimeout(() => { const res = onRejected(self.reason); if (res instanceof myPromise) { res.then(resolve, reject); } else { reject(res); } }); } catch (error) { reject(error); } } }); }
catch(onRejected) { return this.then(null, onRejected); }
static reject(reason) { return new myPromise((resolve, reject) => reject(reason)); } static resolve(value) { if (value instanceof myPromise) return value; else return new myPromise((resolve, reject) => resolve(value)); }
static all(promises) { let promisesLength = promises.length; let count = 0; const result = new Array(promisesLength); return new myPromise((resolve, reject) => { promises.forEach((promise, i) => { myPromise .resolve(promise) .then((res) => { result[i] = res; count++; if (count === promisesLength) { resolve(result); } }) .catch((err) => { reject(err); }); }); }); } static race(promises) { return new myPromise((resolve, reject) => { promises.forEach((promise) => { myPromise .resolve(promise) .then((res) => { resolve(res); }) .catch((err) => { reject(err); }); }); }); } }
let promise1 = new myPromise((resolve, reject) => { setTimeout(() => { resolve(1); }, 1000); }); let promise2 = new myPromise((resolve, reject) => { setTimeout(() => { resolve(2); }, 2000); }); let promise3 = new myPromise((resolve, reject) => { setTimeout(() => { resolve(3); }, 3000); });
myPromise.all([1, 2, 3]).then(([res1, res2, res3]) => { console.log(res1, res2, res3); });
myPromise.race([1, 2, 3]).then((res) => { console.log(res); });
|
前言
写这篇文章的目的是解剖 Promise 源码,起因也是最近秋招被问到了让手写 Promise,另外在网上看到的 Promise 源码或多或少有些小问题,也就是没有完全遵循 Promise/A+规范。
代码会完全使用ES6
语法,主要分以下几个模块:
- 整体分析(为代码书写铺路)
- 实现初版(构造函数大致功能亿完善)
- 支持异步和链式调用(完善 then 方法)
- 实现 catch 方法
- 实现 Promise.resolve()
- 实现 Promise.reject()
- 实现 Promise.all()
- 实现 Promise.race()
一、整体分析
所谓 Promise 就是一个容器,有三个状态:PENDING(进行中)、FULFILLED(成功)、REJECTED(失败),里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,有两大特点:
- 容器状态不受外界影响
- 一旦状态改变就不会再变,任何时候都可以得到这个结果
来看下 Promise 的用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| new Promise((resolve, reject) => { }).then( res => { }, err => { } ).then( res => {
} ).catch( err => console.log(err) ) Promise.resolve(); Promise.reject(); Promise.all([promise1, promise2, ...]).then(); Promise.race([promise1, promise2, ...]).then(); 复制代码
|
通过用法不难分析出:
- Promise 构造函数接受一个函数参数exector,exector 接受resolve和reject两个函数并立即执行,通过resolve/reject改变状态
- 状态改变后,触发原型链上的
then、catch
方法
- Promise 类拥有静态方法
resolve、reject、all、race
那么可以写出大致结构代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Promise { constructor(exector) { const resolve = () => { } const reject = () => { } exector(resolve, reject); } then() { } catch() { } static resolve() { } static reject() { } static all() { } static race() {
} } 复制代码
|
之后在此基础上补充代码。
二、实现初版
首先引入三种状态,完善resolve
、reject
函数,最后在构造函数内执行exector(resolve, 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 26 27 28 29 30 31
| const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; class Promise { constructor(exector) { this.status = PENDING; this.value = undefined; this.reason = undefined; const resolve = value => { if (this.status === PENDING) { this.status = FULFILLED; this.value = value; } } const reject = reason => { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; } } exector(resolve, reject); } } 复制代码
|
注意:exector(resolve, reject);
执行可能会报错,所以需要使用try
包括一下,有报错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 26 27 28 29 30 31
| constructor(exector) { this.status = PENDING; this.value = undefined; this.reason = undefined; const resolve = value => { if (this.status === PENDING) { this.status = FULFILLED; this.value = value; } } const reject = reason => { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; } } try { exector(resolve, reject); } catch(e) { reject(e); } } 复制代码
|
此时可以使用then
进行捕获了,then
接收两个函数,分别对应FULFILLED
和REJECTED
状态:
1 2 3 4 5
| new Promise().then( res => {}, err => {}, ) 复制代码
|
注意:then
、catch
是微任务,这里使用setTimeout模拟:
1 2 3 4 5 6 7 8 9 10 11 12 13
| then(onFulfilled, onRejected) { setTimeout(() => { if (this.status === FULFILLED) { onFulfilled(this.value); } else if (this.status === REJECTED) { onRejected(this.reason); } }) } 复制代码
|
OK,初版已经完成:
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
| const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; class Promise { constructor(exector) { this.status = PENDING; this.value = undefined; this.reason = undefined; const resolve = value => { if (this.status === PENDING) { this.status = FULFILLED; this.value = value; } } const reject = reason => { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; } } try { exector(resolve, reject); } catch(e) { reject(e); } } then(onFulfilled, onRejected) { setTimeout(() => { if (this.status === FULFILLED) { onFulfilled(this.value); } else if (this.status === REJECTED) { onRejected(this.reason); } }) } } 复制代码
|
可以拿数据测试一下:
1 2 3 4 5 6 7
| const promise = new Promise((resolve, reject) => { Math.random() < 0.5 ? resolve(1) : reject(-1); }).then( res => console.log(res), err => console.log(err), ) 复制代码
|
三、支持异步和链式调用
此时初版还有三个方向需要完善:
- Promise 内部异步代码执行的问题。
- Promise 的链式调用
- 值传透
支持异步代码
开发中经常会将接口放于promise
内部,等接口请求响应成功把数据resolve
出去,或失败时把数据reject
出去,此时then
、catch
才会进行捕获。
而现在的代码,promise
内部如果有异步代码执行后才resolve
,then
不会等待异步代码执行完毕会直接执行,所以此时状态是PENDING
,不会触发then
的回调函数。
新增onFulfilledCallbacks、onRejectedCallbacks维护成功态、失败态任务队列:
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
| const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; class Promise { constructor(exector) { this.status = PENDING; this.value = undefined; this.reason = undefined;
this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = value => { if (this.status === PENDING) { this.status = FULFILLED; this.value = value; this.onFulfilledCallbacks.forEach(fn => fn(this.value)); } } const reject = reason => { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } } try { exector(resolve, reject); } catch(e) { reject(e); } } then(onFulfilled, onRejected) { setTimeout(() => { if (this.status === PENDING) { this.onFulfilledCallbacks.push(onFulfilled); this.onRejectedCallbacks.push(onRejected); }else if (this.status === FULFILLED) { onFulfilled(this.value); } else if (this.status === REJECTED) { onRejected(this.reason); } }) } } const promise = new Promise((resolve, reject) => { setTimeout(() => resolve(1), 1000); }).then( res => console.log(res) )
复制代码
|
实现链式调用
Promise
的一大优势就是支持链式调用,具体来说就是then
方法的具体实现,实际上是返回了一个Promise
,需要注意的几个点:
- 保存之前 promise 实例的引用,即保存
this
- 根据
then
回调函数执行的返回值
- 如果是 promise 实例,那么返回的下一个 promise 实例会等待这个 promise 状态发生变化
- 如果不是 promise 实例,根据目前情况直接执行
resolve
或reject
完善then
函数:
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
| then(onFulfilled, onRejected) { // 保存this const self = this; return new Promise((resolve, reject) => { if (self.status === PENDING) { self.onFulfilledCallbacks.push(() => { // try捕获错误 try { // 模拟微任务 setTimeout(() => { const result = onFulfilled(self.value); // 分两种情况: // 1. 回调函数返回值是Promise,执行then操作 // 2. 如果不是Promise,调用新Promise的resolve函数 result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }); self.onRejectedCallbacks.push(() => { // 以下同理 try { setTimeout(() => { const result = onRejected(self.reason); // 不同点:此时是reject result instanceof Promise ? result.then(resolve, reject) : reject(result); }) } catch(e) { reject(e); } }) } else if (self.status === FULFILLED) { setTimeout(() => { try { const result = onFulfilled(self.value); result instanceof Promise ? result.then(resolve, reject) : resolve(result); } catch(e) { reject(e); } }); } else if (self.status === REJECT){ setTimeout(() => { try { const result = onRejected(self.error); result instanceof Promise ? result.then(resolve, reject) : reject(result); } catch(e) { reject(e); } }) } }) } 复制代码
|
值传透
Promise
支持值穿透:
1 2 3 4 5 6 7 8 9 10
| let promsie = new Promise((resolve,reject)=>{ resolve(1) }) .then(2) .then(3) .then(value => { console.log(value) })
复制代码
|
then
参数期望是函数,传入非函数则会发生值穿透。值传透可以理解为,当传入 then 的不是函数的时候,这个 then 是无效的。
原理上是当 then 中传入的不算函数,则这个promise
返回上一个promise
的值,这就是发生值穿透的原因,所以只需要对then
的两个参数进行设置就行了:
1 2 3 4
| onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function'? onRejected: reason => { throw new Error(reason instanceof Error ? reason.message:reason) } 复制代码
|
完整的 then 函数代码:
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
| then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function'? onRejected: reason => { throw new Error(reason instanceof Error ? reason.message:reason) } // 保存this const self = this; return new Promise((resolve, reject) => { if (self.status === PENDING) { self.onFulfilledCallbacks.push(() => { // try捕获错误 try { // 模拟微任务 setTimeout(() => { const result = onFulfilled(self.value); // 分两种情况: // 1. 回调函数返回值是Promise,执行then操作 // 2. 如果不是Promise,调用新Promise的resolve函数 result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }); self.onRejectedCallbacks.push(() => { // 以下同理 try { setTimeout(() => { const result = onRejected(self.reason); // 不同点:此时是reject result instanceof Promise ? result.then(resolve, reject) : reject(result); }) } catch(e) { reject(e); } }) } else if (self.status === FULFILLED) { try { setTimeout(() => { const result = onFulfilled(self.value); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }); } catch(e) { reject(e); } } else if (self.status === REJECTED){ try { setTimeout(() => { const result = onRejected(self.reason); result instanceof Promise ? result.then(resolve, reject) : reject(result); }) } catch(e) { reject(e); } } }); } 复制代码
|
四、实现 catch()方法
Promise.prototype.catch
就是Promise.prototype.then(null, onRejected)
的别名,所以实现就很简单了:
1 2 3 4
| catch(onRejected) { return this.then(null, onRejected); } 复制代码
|
五、Promise.resolve()
这里就不考虑参数是thenable
对象了,那么参数有两种情况:
Promise
实例
- 不是
Promise
实例
1 2 3 4 5 6 7 8 9 10
| static resolve(value) { if (value instanceof Promise) { return value; } else { return new Promise((resolve, reject) => resolve(value)); } } 复制代码
|
六、Promise.reject()
Promise.reject
也会返回一个 Promise 实例,状态为REJECTED
。
与Promise.resolve
不同的是,Promise.reject
方法的参数会原封不动地作为reject
的参数
1 2 3 4 5 6
| static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } 复制代码
|
七、Promise.all()
返回一个 promise 对象,只有当所有 promise 都成功时返回的 promise 状态才成功,需要注意的点是:
- 所有的 promise 状态变为
FULFILLED
,返回的 promise 状态才变为FULFILLED
。
- 一个 promise 状态变为
REJECTED
,返回的 promise 状态就变为REJECTED
。
- 数组成员不一定都是 promise,需要使用
Promise.resolve()
处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| static all(promiseArr) { const len = promiseArr.length; const values = new Array(len); let count = 0; return new Promise((resolve, reject) => { for (let i = 0; i < len; i++) { Promise.resolve(promiseArr[i]).then( val => { values[i] = val; count++; if (count === len) resolve(values); }, err => reject(err), ); } }) } 复制代码
|
八、Promise.race()
Promise.race()
实现就比较简单了:
1 2 3 4 5 6 7 8 9 10 11
| static race(promiseArr) { return new Promise((resolve, reject) => { promiseArr.forEach(p => { Promise.resolve(p).then( val => resolve(val), err => reject(err), ) }) }) } 复制代码
|
九、完整代码
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
| const PENDING = 'PENDING'; const FULFILLED = 'FULFILLED'; const REJECTED = 'REJECTED'; class Promise { constructor(exector) { this.status = PENDING; this.value = undefined; this.reason = undefined; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; const resolve = value => { if (this.status === PENDING) { this.status = FULFILLED; this.value = value; this.onFulfilledCallbacks.forEach(fn => fn(this.value)); } } const reject = reason => { if (this.status === PENDING) { this.status = REJECTED; this.reason = reason; this.onRejectedCallbacks.forEach(fn => fn(this.reason)) } } try { exector(resolve, reject); } catch(e) { reject(e); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value; onRejected = typeof onRejected === 'function'? onRejected: reason => { throw new Error(reason instanceof Error ? reason.message:reason) } const self = this; return new Promise((resolve, reject) => { if (self.status === PENDING) { self.onFulfilledCallbacks.push(() => { try { setTimeout(() => { const result = onFulfilled(self.value); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }) } catch(e) { reject(e); } }); self.onRejectedCallbacks.push(() => { try { setTimeout(() => { const result = onRejected(self.reason); result instanceof Promise ? result.then(resolve, reject) : reject(result); }) } catch(e) { reject(e); } }) } else if (self.status === FULFILLED) { try { setTimeout(() => { const result = onFulfilled(self.value); result instanceof Promise ? result.then(resolve, reject) : resolve(result); }); } catch(e) { reject(e); } } else if (self.status === REJECTED){ try { setTimeout(() => { const result = onRejected(self.reason); result instanceof Promise ? result.then(resolve, reject) : reject(result); }) } catch(e) { reject(e); } } }); } catch(onRejected) { return this.then(null, onRejected); } static resolve(value) { if (value instanceof Promise) { return value; } else { return new Promise((resolve, reject) => resolve(value)); } } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }) } static all(promiseArr) { const len = promiseArr.length; const values = new Array(len); let count = 0; return new Promise((resolve, reject) => { for (let i = 0; i < len; i++) { Promise.resolve(promiseArr[i]).then( val => { values[i] = val; count++; if (count === len) resolve(values); }, err => reject(err), ); } }) } static race(promiseArr) { return new Promise((resolve, reject) => { promiseArr.forEach(p => { Promise.resolve(p).then( val => resolve(val), err => reject(err), ) }) }) } }
|
作者:洛霞
链接:https://juejin.im/post/6860037916622913550
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。