手把手实现Promise
基本结构
- Promise 构造函数传入一个函数
new Promise((resolve, reject) => resolve(1))
- 函数中有两个参数为
resolve
和reject
的函数 - Promise 返回一个含有
then
方法的实例p.then()
then
方法可以传入两个函数分别是resolved
来获取Promise
成功状态的值,和rejected
来获取Promise
失败的值.
基本结构代码如下:
function Promise(executor) { const resolve = () => { } const reject = () => { } // 执行传入的函数参数 executor(resolve, reject); } // 增加then方法 Promise.prototype.then = function (onResolved, onRejected) { }
增加状态和返回值
基本结构搭建好后,我们知道在使用Promise
中不管是resolve
还是reject
最终都只会出现一种状态和对应的返回结果值。例如下面例子:
// resolve值 只会执行resolve new Promise(resolve, reject => { resolve('success'); }); // reject值 只会执行reject new Promise(resolve, reject => { reject('reject'); }); // reject值 只会执行resolve() let p = new Promise((resolve, reject) => { resolve(1); reject(2); }); p.then(data => { console.log(data); //1 }, error => { console.log(error); })
接着继续完善Promise
:
- 增加一个变量
status
记录当前实例状态,三种状态pending、fulfilled、rejected
,默认pending
- 增加一个变量储存
resolve
值 - 增加一个变量储存
reject
值
function Promise(executor) { this.status = 'pending'; //状态默认为等待 this.value = null; // resolve值 this.reason = null; // reject值 const resolve = value => { this.value = value; } const reject = reason => { this.reason = reason; } executor(resolve, reject); } Promise.prototype.then = function (onResolved, onRejected) { onResolved(this.value); onRejected(this.reason); }
这里then放在原型上是因为实例可以直接共享一份原型,而不是每次都需要新建,这样减少内存使用
状态唯一性
上面代码在then
实现中会执行resolve
和reject
两个,会有两个返回值,在真实的Promise
中只会出现一种状态,接下来需要做的是状态的处理:
// 增加状态处理的逻辑 function Promise(executor) { this.status = 'pending'; //状态默认为等待 this.value = null; // resolve值 this.reason = null; // reject值 const resolve = value => { // 只有进入等待状态 才能进入解决状态 if (this.status === 'pending') { this.value = value; this.status = 'fulfilled'; } } const reject = reason => { // 只有进入等待状态,才能进入拒绝状态 if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; } } executor(resolve, reject); } Promise.prototype.then = function (onResolved, onRejected) { if (this.status === 'fulfilled') { onResolved(this.value); } if (this.status === 'rejected') { onRejected(this.reason); } }
上面Promise已经是能正常运行起来了,能正常的输出结果,算是一个初级版本了。
但是如果我们的Promise
执行下面例子:
let p = new Promise((resolve, reject) => { setTimeout(() => { resolve('res'); }, 3000) }); p.then(res => console.log(res)); // 没有输出
并没有输出结果,因为我们的Promise
代码是同步的,而例子中使用异步setTimeout
,而then
方法的实现不会等待异步的fulfilled
状态,而是在pending
状态的时候直接返回了空。
增加异步的支持
解决上面例子的中异步问题,我们需要在then
中保存resolve
和reject
的回调函数,然后在状态变化时候来调用,调整如下:
function Promise(executor) { this.status = 'pending'; //状态默认为等待 this.value = null; // resolve值 this.reason = null; // reject值 this.onResolvedFunc = Function.prototype; // 使用Function原型作为默认函数值 this.onRejectedFunc = Function.prototype; const resolve = value => { // 只有进入等待状态 才能进入解决状态 并触发回调 if (this.status === 'pending') { this.value = value; this.status = 'fulfilled'; this.onResolvedFunc(this.value); } } const reject = reason => { // 只有进入等待状态,才能进入拒绝状态 并触发回调 if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; this.onRejectedFunc(this.reason); } } executor(resolve, reject); } Promise.prototype.then = function (onResolved, onRejected) { if (this.status === 'fulfilled') { onResolved(this.value); } if (this.status === 'rejected') { onRejected(this.reason); } // 储存resolve, reject回调函数 if (this.status === 'pending') { this.onResolvedFunc = onResolved; this.onRejectedFunc = onRejected; } }
现在我们的Promise
是支持异步了,但是现在又面临新的问题,我们知道Promise
中then
函数中的代码是异步的,请看下面例子:
let p = new Promise((resolve, reject) => { resolve('2'); }); p.then(res => console.log(res)); console.log('1');
官方Promise
上面例子是先输出1
再输出2
,我们的版本是2
和1
。
我们需要如何实现then
中代码异步化,很简单,放在setTimeout
中执行就可以了,调整后的Promise
代码:
function Promise(executor) { this.status = 'pending'; //状态默认为等待 this.value = null; // resolve值 this.reason = null; // reject值 this.onResolvedFunc = Function.prototype; // 使用Function原型作为默认函数 this.onRejectedFunc = Function.prototype; const resolve = value => { // 只有进入等待状态 才能进入解决状态 并触发回调 setTimeout(() => { if (this.status === 'pending') { this.value = value; this.status = 'fulfilled'; this.onResolvedFunc(this.value); } }); } const reject = reason => { // 只有进入等待状态,才能进入拒绝状态 并触发回调 setTimeout(() => { if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; this.onRejectedFunc(this.reason); } }); } executor(resolve, reject); } Promise.prototype.then = function (onResolved, onRejected) { if (this.status === 'fulfilled') { onResolved(this.value); } if (this.status === 'rejected') { onRejected(this.reason); } // 储存resolve, reject回调函数 if (this.status === 'pending') { this.onResolvedFunc = onResolved; this.onRejectedFunc = onRejected; } }
处理多个then的问题
在正常的Promise
中会出现连续调用多个then,例如:
let p = new Promise((resolve, reject) => { resolve(1); }); p.then(res => console.log("res1:", res)); //res1: 1 p.then(res => console.log("res2:", res)); //res2: 1
而我们的Promise
只会输出后面的结果,原因是第二次的then
覆盖了之前的then
,所以最终只会执行一个,我们需要改成数组的形式存放reject和resolve
即可,调整如下:
function Promise(executor) { this.status = 'pending'; //状态默认为等待 this.value = null; // resolve值 this.reason = null; // reject值 this.onResolvedArray = []; //数组存放 this.onRejectedArray = []; const resolve = value => { // 只有进入等待状态 才能进入解决状态 并触发回调 setTimeout(() => { if (this.status === 'pending') { this.value = value; this.status = 'fulfilled'; // 调用所有的then resolve this.onResolvedArray.forEach(fun => fun(this.value)); } }); } const reject = reason => { // 只有进入等待状态,才能进入拒绝状态 并触发回调 setTimeout(() => { if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; // 调用所有的then reject this.onRejectedArray.forEach(fun => fun(this.reason)); } }); } executor(resolve, reject); } Promise.prototype.then = function (onResolved, onRejected) { if (this.status === 'fulfilled') { onResolved(this.value); } if (this.status === 'rejected') { onRejected(this.reason); } // 储存resolve, reject回调函数 if (this.status === 'pending') { // 保存resolve reject this.onResolvedArray.push(onResolved); this.onRejectedArray.push(onRejected); } }
现在我们的Promise
可以执行多个then
了。
增加链式调用支持
看一个例子:
const p = new Promise((resolve, reject) => { resolve(1); }); //1.支持 then后面接着调用then p.then(res => { console.log(res); }) .then(res => { console.log(222); }); // 依次输出 1 2
思路:
- 要实现
then
的链式调用必须在then
函数中返回一个Promise
才能继续支持调用then
- 也就是说
then
中的resolve
和reject
函数中中必须返回Promise
- 我们将
status
三种状态的执行结果保存通过一个新的Promise
返回
调整后:
Promise.prototype.then = function (onResolved, onRejected) { if (this.status === 'fulfilled') { // 返回一个新的Promise实例 return new Promise((resolve, reject) => { setTimeout(() => { try { // 保存resolve执行的结果 let result = onResolved(this.value); resolve(result); } catch (e) { reject(e); } }); }); } if (this.status === 'rejected') { return new Promise((resolve, reject) => { setTimeout(() => { try { let result = onRejected(this.reason); resolve(result); } catch (e) { reject(e); } }) }) } // 储存resolve, reject回调函数 if (this.status === 'pending') { // 保存resolve reject return new Promise((resolve, reject) => { this.onResolvedArray.push(() => { try { let result = onResolved(this.value); resolve(result); } catch (e) { reject(e) } }); this.onRejectedArray.push(() => { try { let result = onRejected(this.reason); resolve(result); } catch (e) { reject(e) } }); }); } }
三种状态下的
resolve、reject
函数都需要通过新的Promise
包装一层在返回结果
这样确实可以支持then
链式调用了,但是有一种情况,如下例子:
let p4 = new Promise((resolve, reject) => { resolve(1) }); p4.then(res => { console.log(res); return new Promise((resolve, reject) => { resolve(2); }); }) .then(res => { console.log(res) }) .then(res => { console.log(4) })
// 新增一个函数处理Promise 和 普通值 const resolvePromise = (promise, result, resolve, reject) => { // 当promise 和 result相等 if (promise === result) { reject(new TypeError('error due to circular reference')); } let consumed = false,// 是否已经执行 thenable = null; // 保存then函数 // 如果result是Promise 实例 if (result instanceof Promise) { if (result.status === 'pending') { result.then(function (data) { // 继续递归调用 resolvePromise(promise, data, resolve, reject); }, reject) } else { result.then(resolve, reject); } return; } const isComplexResult = target => (typeof target === 'function' || typeof target === 'object') && (target !== null); // 如果result 疑似 Promise if (isComplexResult(result)) { try { thenable = result.then; // 如果result有then方法 if (typeof thenable === 'function') { thenable.call(result, function (data) { if (consumed) { return; } consumed = true; // 递归 return resolvePromise(promise, data, resolve, reject); }, function(error) { if (consumed) return; consumed = true; return reject(error); }) } } catch (e) { if (consumed) return; consumed = true; return reject(e); } } else { resolve(result); } }
promise: 需要返回的Promise
result: reject 或 resolve 执行的结果
resolve: 需要返回的Promise 的 resolve
reject: 需要返回的Promise 的 reject
所谓疑似就是result是function或者对象并且有then方法,我们也当做Promise来处理
上面代码实现 多次使用递归和 pending状态处理,需要反复理解才能想通 [dog]
接着在then
中result
返回值使用resolvePromise
处理:
Promise.prototype.then = function (onResolved, onRejected) { // 保存promise let promise = null; if (this.status === 'fulfilled') { // 返回一个新的Promise实例 return promise = new Promise((resolve, reject) => { setTimeout(() => { try { // 保存resolve执行的结果 let result = onResolved(this.value); resolvePromise(promise, result, resolve, reject); } catch (e) { reject(e); } }); }); } if (this.status === 'rejected') { return promise = new Promise((resolve, reject) => { setTimeout(() => { try { let result = onRejected(this.reason); resolvePromise(promise, result, resolve, reject); } catch (e) { reject(e); } }) }) } // 储存resolve, reject回调函数 if (this.status === 'pending') { // 保存resolve reject return promise = new Promise((resolve, reject) => { this.onResolvedArray.push(() => { try { let result = onResolved(this.value); resolvePromise(promise, result, resolve, reject); } catch (e) { reject(e) } }); this.onRejectedArray.push(() => { try { let result = onRejected(this.reason); resolvePromise(promise, result, resolve, reject); } catch (e) { reject(e) } }); }); } }
完整代码:
function Promise(executor) { this.status = 'pending'; //状态默认为等待 this.value = null; // resolve值 this.reason = null; // reject值 this.onResolvedArray = []; //数组存放 this.onRejectedArray = []; const resolve = value => { if (value instanceof Promise) { return value.then(resolve, reject); } // 只有进入等待状态 才能进入解决状态 并触发回调 setTimeout(() => { if (this.status === 'pending') { this.value = value; this.status = 'fulfilled'; // 调用所有的then resolve this.onResolvedArray.forEach(fun => fun(value)); } }); } const reject = reason => { // 只有进入等待状态,才能进入拒绝状态 并触发回调 setTimeout(() => { if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; // 调用所有的then reject this.onRejectedArray.forEach(fun => fun(reason)); } }); } executor(resolve, reject); } const resolvePromise = (promise2, result, resolve, reject) => { // 当promise 和 result相等 直接返回错误 if (result === promise2) { reject(new TypeError('error due to circular reference')); } let consumed = false; //是否执行了 let thenable; // 保存then函数 // 如果result是Promise 实例 if (result instanceof Promise) { if (result.status === 'pending') { result.then(function (data) { // 继续递归调用 resolvePromise(promise2, data, resolve, reject); }, reject); } else { result.then(resolve, reject); } return; } let isComplexResult = target => (typeof target === 'function' || typeof target === 'object') && (target !== null); // 如果result 疑似 Promise if (isComplexResult(result)) { try { thenable = result.then; // 如果result有then方法 if (typeof thenable === 'function') { thenable.call(result, function (data) { if (consumed) { return; } consumed = true; // 递归 return resolvePromise(promise2, data, resolve, reject); }, function(error) { if (consumed) return; consumed = true; return reject(error); }) } else { resolve(result); } } catch (e) { if (consumed) return; consumed = true; return reject(e); } } else { resolve(result); } } Promise.prototype.then = function (onResolved, onRejected) { onResolved = typeof onResolved === 'function' ? onResolved : data => data; onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }; // 保存promise let promise2 = null; if (this.status === 'fulfilled') { // 返回一个新的Promise实例 return promise2 = new Promise((resolve, reject) => { setTimeout(() => { try { // 保存resolve执行的结果 let result = onResolved(this.value); resolvePromise(promise2, result, resolve, reject); } catch (e) { reject(e); } }); }); } if (this.status === 'rejected') { return promise2 = new Promise((resolve, reject) => { setTimeout(() => { try { let result = onRejected(this.reason); resolvePromise(promise2, result, resolve, reject); } catch (e) { reject(e); } }) }) } // 储存resolve, reject回调函数 if (this.status === 'pending') { // 保存resolve reject return promise2 = new Promise((resolve, reject) => { this.onResolvedArray.push(() => { try { let result = onResolved(this.value); resolvePromise(promise2, result, resolve, reject); } catch (e) { reject(e) } }); this.onRejectedArray.push(() => { try { let result = onRejected(this.reason); resolvePromise(promise2, result, resolve, reject); } catch (e) { reject(e) } }); }); } }
上面代码增加一些参数容错处理onResolved、onRejected
设置默认参数,resolve
函数,value
如果是Promise直接调用
onResolved = typeof onResolved === 'function' ? onResolved : data => data; onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }; const resolve = value => { if (value instanceof Promise) { return value.then(resolve, reject); } }
Promise 穿透现象
什么是穿透现象,看下面例子:
let p = new Promise((resolve, reject) => { resolve(1); }); p.then(null) .then(() => { console.log(2) }); // 2
简而言之如果第一个then函数是null,会跳过当前then继续往下执行
我们实现的Promise中已经支持穿透现象,当参数为null
给onResolved、onRejected
设置默认函数即可。
Promise.prototype.then = function (onResolved, onRejected) { onResolved = typeof onResolved === 'function' ? onResolved : data => data; onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }; //... }
静态方法的支持
- Promise.prototype.catch
- Promise.resolve
- Promise.reject
- Promise.all
- Promise.race
Promise.prototype.catch
// demo let promise1 = new Promise((res, rej) => { rej('error'); }); promise1.then(data => { console.log(data) }).catch(error => { console.log(error); // error }); // 实现 Promise.prototype.catch = function (catchFunc) { return this.then(null, catchFunc); }
使用then的第二个参数来捕获来实现
Promise.resolve 和 Promise.reject
使用场景:
Promise.resolve('res').then(data => { console.log(data); }) console.log(1);
Promise.resolve
返回新的Promise
实例和实例的resolve
函数执行后的结果,reject
同理,实现代码:
Promise.resolve = function (value) { return new Promise((resolve, reject) => { resolve(value); }); } Promise.reject = function (value) { return new Promise((resolve, reject) => { reject(value); }); }
Promise.all
Promise.all
返回一个Promise
,接受一个Promise
数组, 当所有Promise
都resolve
则返回resolve
,有一个失败返回reject
.
看下例子:
let p1 = new Promise((resolve, reject) => { resolve(1); }); let p2 = new Promise((resolve, reject) => { resolve(2); }); Promise.all([p1, p2]).then(res => { console.log(res); });
执行结果输出:[1, 2]
.
实现思路:
- 遍历执行所有的
Promise
- 保存每一个
Promise
的resolve
执行结果 - 执行完成后返回执行结果
- 如果遍历的时候有一个出现
reject
则直接返回reject
实现代码:
Promise.all = function (promiseArray) { // 非数组报错 if (!Array.isArray(promiseArray)) { throw new TypeError('arguments should be an array'); } return new Promise((resolve, reject) => { try { let resultArray = []; const length = promiseArray.length; for (let i=0; i<length; i++) { // 执行Promise.then promiseArray[i].then(data => { // 保存结果 resultArray.push(data); // 完成所有promise resolve结果 if (resultArray.length === length) { resolve(resultArray); } // 其中有一个出现reject 则返回reject }, reject); } } catch(e) { // 出现异常直接reject reject(e); } }); }
Promise.race
和all
不一样,all
是所有Promise
都resolve才resolve,而race是当有一个Promise为先完成resolve就resolve.这里是竞争关系,实现思路是直接执行所有的Promise即可,谁先完成就会先返回
实现代码:
Promise.race = function(promiseArray) { if (!Array.isArray(promiseArray)) { throw new TypeError('arguments should be an array'); } return new Promise((resolve, reject) => { try { for (let i=0; i<promiseArray.length; i++) { promiseArray[i].then(resolve, reject); } } catch (e) { reject(e); } }); }
一些细节问题
完成Promise
静态方法之后Promise
所有的功能就已经完成了,还需要处理一些细节问题:
executor 使用try catch包裹
function Promise(executor) { // executor 使用try catch包裹 try { executor(resolve, reject); } catch (e) { reject(e); } }
完整代码
function Promise(executor) { this.status = 'pending'; //状态默认为等待 this.value = null; // resolve值 this.reason = null; // reject值 this.onResolvedArray = []; //数组存放 this.onRejectedArray = []; const resolve = value => { if (value instanceof Promise) { return value.then(resolve, reject); } // 只有进入等待状态 才能进入解决状态 并触发回调 setTimeout(() => { if (this.status === 'pending') { this.value = value; this.status = 'fulfilled'; // 调用所有的then resolve this.onResolvedArray.forEach(fun => fun(value)); } }); } const reject = reason => { // 只有进入等待状态,才能进入拒绝状态 并触发回调 setTimeout(() => { if (this.status === 'pending') { this.reason = reason; this.status = 'rejected'; // 调用所有的then reject this.onRejectedArray.forEach(fun => fun(reason)); } }); } try { executor(resolve, reject); } catch (e) { reject(e); } } const resolvePromise = (promise2, result, resolve, reject) => { // 当promise 和 result相等 直接返回错误 if (result === promise2) { reject(new TypeError('error due to circular reference')); } let consumed = false; //是否执行了 let thenable; // 保存then函数 // 如果result是Promise 实例 if (result instanceof Promise) { if (result.status === 'pending') { result.then(function (data) { // 继续递归调用 resolvePromise(promise2, data, resolve, reject); }, reject); } else { result.then(resolve, reject); } return; } let isComplexResult = target => (typeof target === 'function' || typeof target === 'object') && (target !== null); // 如果result 疑似 Promise if (isComplexResult(result)) { try { thenable = result.then; // 如果result有then方法 if (typeof thenable === 'function') { thenable.call(result, function (data) { if (consumed) { return; } consumed = true; // 递归 return resolvePromise(promise2, data, resolve, reject); }, function(error) { if (consumed) return; consumed = true; return reject(error); }) } else { resolve(result); } } catch (e) { if (consumed) return; consumed = true; return reject(e); } } else { resolve(result); } } Promise.prototype.then = function (onResolved, onRejected) { onResolved = typeof onResolved === 'function' ? onResolved : data => data; onRejected = typeof onRejected === 'function' ? onRejected : error => { throw error }; // 保存promise let promise2 = null; if (this.status === 'fulfilled') { // 返回一个新的Promise实例 return promise2 = new Promise((resolve, reject) => { setTimeout(() => { try { // 保存resolve执行的结果 let result = onResolved(this.value); resolvePromise(promise2, result, resolve, reject); } catch (e) { reject(e); } }); }); } if (this.status === 'rejected') { return promise2 = new Promise((resolve, reject) => { setTimeout(() => { try { let result = onRejected(this.reason); resolvePromise(promise2, result, resolve, reject); } catch (e) { reject(e); } }) }) } // 储存resolve, reject回调函数 if (this.status === 'pending') { // 保存resolve reject return promise2 = new Promise((resolve, reject) => { this.onResolvedArray.push(() => { try { let result = onResolved(this.value); resolvePromise(promise2, result, resolve, reject); } catch (e) { reject(e) } }); this.onRejectedArray.push(() => { try { let result = onRejected(this.reason); resolvePromise(promise2, result, resolve, reject); } catch (e) { reject(e) } }); }); } } // catch Promise.prototype.catch = function (catchFunc) { return this.then(null, catchFunc); } // resolve Promise.resolve = function (value) { return new Promise((resolve, reject) => { resolve(value); }); } //reject Promise.reject = function (value) { return new Promise((resolve, reject) => { reject(value); }); } //all Promise.all = function (promiseArray) { // 非数组报错 if (!Array.isArray(promiseArray)) { throw new TypeError('arguments should be an array'); } return new Promise((resolve, reject) => { try { let resultArray = []; const length = promiseArray.length; for (let i=0; i<length; i++) { promiseArray[i].then(data => { resultArray.push(data); // 完成所有promise resolve结果 if (resultArray.length === length) { resolve(resultArray); } // 其中有一个出现reject 则返回reject }, reject); } } catch(e) { // 出现异常直接reject reject(e); } }); } //race Promise.race = function(promiseArray) { if (!Array.isArray(promiseArray)) { throw new TypeError('arguments should be an array'); } return new Promise((resolve, reject) => { try { for (let i=0; i<promiseArray.length; i++) { promiseArray[i].then(resolve, reject); } } catch (e) { reject(e); } }); }
总结
链式调用resolvePromise
实现比较难理解,需要反复阅读体会。
引用
书籍:前端开发核心知识进阶:50 讲从夯实基础到突破瓶颈