promise内部实现
Posted mcgee0731
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了promise内部实现相关的知识,希望对你有一定的参考价值。
从本文你将了解到
- 什么是promise
promise的内部实现
- resolve实例属性
- reject实例属性
- then方法
- then方法的多次调用
- then的链式调用
- 错误捕获
try{}catch(e){}
- then可选参数
- 静态方法 all 的实现
- 静态方法 resolve 的实现
- 实例方法 finally 的实现
实例方法 catch 的实现
什么是promise
步骤分析
/** * 1.Promise是个类,参数是个回调函数(执行器),这个执行器会立即执行 * 2.promise中有三种状态,成功fulfilled,失败rejected,等待pending,一旦状态确定,则不可更改 * pending -> fulfilled * pending -> rejected * 3.resolve,reject函数是用来改变状态的 * resolve() pending -> fulfilled * reject() pending -> rejected * 4.then方法内部做的事情就是判断状态 如果状态是成功调用成功的回调函数,如果是失败调用失败的回调函数, * then方法是被定义在原型对象中的 * 5.then成功的回调函数有个参数,表示成功之后的值。then失败的回调函数有个参数,表示失败之后的原因 */ new Promise((resolve,reject)=>{ resolve("成功") // reject("失败") })
实现
实现最基础的功能
声明一个 MyPromise 类,在构造函数中接收一个执行器,并立即执行这个执行器
// 定义状态常量
const PENDING = \'pending\'; // 等待
const FULFILLED = \'fulfilled\'; // 成功
const REJECTED = \'rejected\'; // 失败
class MyPromise {
constructor(executor) {
// 执行器接收两个参数,resolve 和 reject
// 执行器立即执行
executor(this.resolve, this.reject);
}
// 定义实例的状态属性: 初始值为 pending,状态一旦确定,就不可更改
status = PENDING;
}
执行 执行器 的时候,需要传递两个参数,resolve 和 reject。
- 在 执行器 函数体中执行这两个方法的时候是直接调用的
resolve(); reject()
- 直接执行函数的话,这个函数体内部的 this 会指向 window。
- 所以在定义 resolve 和 reject方法的时候使用箭头函数,避免 this 指向的问题,使其能够指向 promise 这个实例。
- 因为在 执行器 函数体中,resolve 和 reject 是用来更改 Promise 的状态的,而且更改之后,不可以再次更改
class MyPromise {
// 构造函数执行器
...
resolve = () => {
// 状态不是 pending 的时候,return。
if (this.status !== PENDING) return;
this.status = FULFILLED;
}
reject = () => {
// 状态不是 pending 的时候,return。
if (this.status !== PENDING) return;
this.status = REJECTED;
}
}
调用 resolve
和 reject
的时候传入了参数:resolve(\'成功后的数据\')
reject(\'失败的原因\')
所以在 resolve
和 reject
中接收对应的参数,并保存到实例属性中
class MyPromise {
// 构造函数执行器
...
// 定义两个属性,
// 成功之后的值
value = undefined
// 失败的原因
reason = undefined
resolve = value => { // 接收成功的数据
// 状态不是 pending 的时候,return。
if (this.status !== PENDING) return;
this.status = FULFILLED;
// 保存成功的值
this.value = value;
}
reject = reason => { // 接收失败的原因
// 状态不是 pending 的时候,return。
if (this.status !== PENDING) return;
this.status = REJECTED;
// 保存失败的原因
this.reason = reason;
}
}
在调用 Promise()
之后,会返回一个 promise 实例,这个实例中有一个 then 方法,用于处理函数内结束的成功或失败的回调
then
有两个参数,一个参数是成功的回调successCallback
,一个是失败的回调failCallback
- 在调用
successCallback
的时候,需要将 成功的数据/值 传递进去- 在调用
failCallback
的时候,需要将 错误的原因 传递进去- 成功的值 和 失败的原因已经用定义的变量
value
和reason
保存过了。
要调用成功的回调还是失败的回调,需要要看 Promise 的状态:
如果是 Fulfilled 调用成功的回调;如果是 Rejected 调用失败的回调;
class MyPromise{
...
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback(this.value);
} else if (this.status === REJECTED) {
failCallback(this.reason);
}
}
}
加入 异步逻辑
如果在使用 Promise 的时候,把 resolve
或 reject
放在异步逻辑中。
// 2s 后再执行成功的 resolve
setTimeout(() => {
resolve(\'成功\')
}, 2000)
这个时候,再使用 then
的话,then
里面判断 promise
状态,此时的 status
还是 padding
。
因为 promise 的状态 status
是靠调用 resolve/reject
来更改的。延迟了 resolve/reject
的调用,就是延迟了状态的更新。
而 then 方法中只判断了 成功和失败 的状态,并没有去判断 等待的状态(也是就异步的状态)
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback(this.value);
} else if (this.status === REJECTED) {
failCallback(this.reason);
}
}
所以我们要做的事情是:如果在调用 then
的时候,promise
的状态是 pending
等待状态,需要将 传递进来的成功的回调和失败的回调 存储起来,以便延迟结束调用。
class MyPromise {
...
// 成功回调
successCallback = undefined;
// 失败回调
failCallback = undefined;
...
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback(this.value);
} else if (this.status === REJECTED) {
failCallback(this.reason);
} else {
// 等待
// 保存成功 和 失败的回调函数
this.successCallback = successCallback;
this.failCallback = failCallback;
}
}
}
之后在异步逻辑结束之后,会调用 resolve 或 reject ,此时就可以在Promise中的 resolve 或 reject 方法里判断是否有对应的回调,如果有就执行它
resolve = value => {
...
// 判断是否有成功的回调
this.successCallback && this.successCallback(this.value)
}
reject = reason => {
...
// 判断是否有失败的回调
this.failCallback && this.failCallback(this.reason);
}
实现 then 方法多次调用 添加多个处理函数
- 每个 Promise 对象的 then 方法都是可以被多次调用的,
- 当 then 方法被调用的时候,每个 then 方法里面的回调函数都是要被调用的
- 如果多次调用了 then 方法,应该先将所有 then 方法里面的回调函数都存储起来,当 Promise 状态发生变化的时候,将依次执行这些回调函数
为了存储多个 then 里面的回调函数,需要将
successCallback
和failCallback
属性改为数组// 成功回调 // successCallback = undefined; successCallback = []; // 失败回调 // failCallback = undefined; failCallback = []; then() { ... // 保存成功 和 失败的回调函数 // this.successCallback = successCallback; // this.failCallback = failCallback; this.successCallback.push(successCallback); this.failCallback.push(failCallback); ... }
然后在 Promise 状态更新的时候,依次调用这些回调函数
resolve = value => { ... // 如果成功回调存在,就执行它 // this.successCallback && this.successCallback(this.value); while (this.successCallback.length) { // 弹出第一个回调函数,并执行 this.successCallback.shift()(this.value); } } reject = reason => { ... // 如果失败回调存在,就执行它 // this.failCallback && this.failCallback(this.reason); while (this.failCallback.length) { // 弹出第一个回调函数,并执行 this.failCallback.shift()(this.reason); } }
使用
promise.then(value => { console.log(1); console.log(value); }); promise.then(value => { console.log(2); console.log(value); }); promise.then(value => { console.log(3); console.log(value); });
结果
1 成功 2 成功 3 成功
then 方法的链式调用
- Promise 的 then 方法是可以被链式调用的
- 每一个 then 方法中回调函数拿到的值,其实是上一个 then 方法回调函数的返回值
// test.js
promise
.then(value => {
console.log(value);
return 100;
})
.then(value => { // 这一个value 就是前面 then 方法回调函数中 return 的100
console.log(value); // 100
});
- 实现 then 方法的链式调用
每一个 then 方法都会返回一个 Promise 对象
then(successCallback, failCallback) {
// 实现 then 方法返回一个 Promise 对象
let promise2 = new MyPromise(() => {
if (this.status === FULFILLED) {
successCallback(this.value);
} else if (this.status === REJECTED) {
failCallback(this.reason);
} else {
// 等待
// 保存成功 和 失败的回调函数
// this.successCallback = successCallback;
// this.failCallback = failCallback;
this.successCallback.push(successCallback);
this.failCallback.push(failCallback);
}
});
// 返回 Promise 对象
return promise2;
}
- 将上一个 then 方法回调函数的返回值传递给下一个 then 方法回调函数
- 想要下一个then接收到上一个then,那么上一个then要返回一个prommise对象
获取回调函数的返回值
// 成功的回调 then(successCallback,failCallback){ ... let promise2 = new MyPromise(() => { if (this.status === FULFILLED) { // 保存成功回调的返回值 let x = successCallback(this.value); } ... }); return promise2 }
将 x 传递给下一个 then。下一个 then 其实就是 promise2 里面的 then,(因为你返回了个promise2,他后面接的then肯定是promise2的呀)
只要调用 promise2 的 resolve方法,那么promise2的then就会执行,同时要传递的 x
通过resolve(x)
就会传递给下一个 then 的回调函数了
// 成功的回调
then(successCallback,failCallback){
// 只要调用 resolve 方法,就能将 x 传递给 then 方法的回调函数
let promise2 = new MyPromise((resolve, reject) => {
let x = successCallback(this.value);
resolve(x);
})
}
then 方法链式调用返回值的判断
- 如果上一个 then 方法回调函数的返回值是一个 普通值,直接 resolve;
- 如果上一个 then 方法回调函数的返回值是一个 Promise 对象,则需要判断这个 Promise 对象的返回结果(状态),并根据状态来决定要使用 resolve 还是 reject
// 成功的回调
then(successCallback,failCallback){
let promise2 = new MyPromise((resolve, reject) => {
let x = successCallback(this.value);
// 判断 x 的值是普通值还是 Promise 对象,
// 如果是普通值,直接调用 resolve
// 如果是 Promise 对象,查看这个 Promise 对象的返回结果,
// 再根据返回结果来决定调用 resolve 还是 reject
resolveRromise(x, resolve, reject);
})
}
function resolveRromise(x, resolve, reject) {
// 使用 instanceof 来判断 x 是否是 MyPromise 的实例对象
if (x instanceof MyPromise) {
// 调用 then 方法来查看 x 的返回值,
// 如果是成功的,就调用then的第一个参数方法,失败则调用then的第二个参数方法
// 简写成执行promise2对象的resolve和reject方法,向下传递
// x.then(value => resolve(value), reason => reject(reason))
x.then(resolve, reject);
} else {
resolve(x)
}
}
测试
function other (){
return new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log("other")
},2000)
})
}
promise.then(value=>{
console.log(value)
return other() //返回一个promise
}).then(value=>{
console.log(value) //输出other
})
then 方法链式调用识别 Promise 对象自返回
如果 then 方法的回调函数中,返回了自己(Promise对象),Promise 就会进入循环调用,这种情况是不被允许的:Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>
var promise1 = new Promise((resolve, reject) => {
resolve(100);
});
var promise2 = promise1.then(value => {
console.log(value); // 100
// 自己返回自己
return promise2;
})
解决方法:判断当前 then 的值 promise2 对象 和 它内部返回给下一个then 的promise对象的返回值 x
,是否相同,如果相同,就是 自反回。
...
then(){
//传入promise2 在resolvePromise中将promise2和x进行判断
resolvePromise(promise2, x, resolve, reject);
}
...
function resolvePromise(promise2, x, resolve, reject) {
// 判断是否是 自反回
if (promise2 === x) {
return reject(new TypeError(\'Chaining cycle detected for promise #<Promise>\'))
}
// 使用 instanceof 来判断 x 是否是 MyPromise 对象
if (x instanceof MyPromise) {
// 调用 then 方法来查看 x 的返回值,
// 如果是成功的,就调用resolve;失败就调用 reject
// x.then(value => resolve(value), reason => reject(reason))
x.then(resolve, reject);
} else {
resolve(x)
}
}
问题:promise2 是new MyPromise((resolve, reject) => {...})
执行完成之后才有的,而现在是在 new promise2 执行的过程中去获取它,是获取不到的。
解决方法就是将 取到回调和比较 promise2 和 x
的代码变为 异步执行。他会在then方法的所有同步代码执行完之后才执行,以便于取到promise2
// 将代码改为异步执行,确保拿到 promise2
setTimeout(() => {
// 保存成功回调的返回值
let x = successCallback(this.value);
// 判断 x 的值是普通值还是 Promise 对象,
// 如果是普通值,直接调用 resolve
// 如果是 Promise 对象,查看这个 Promise 对象的返回结果,
// 再根据返回结果来决定调用 resolve 还是 reject
// resolve(x);
resolveRromise(promise2, x, resolve, reject);
}, 0);
捕获错误及 then 链式调用其他状态代码补充
- 捕获执行器的错误
在执行执行器的时候,使用 try/catch 捕获异常
当传入的执行器是个error不是正常的执行器时候获取e.message
class MyPromise { constructor(executor) { try { executor(this.resolve, this.reject); } catch (err) { this.reject(err); } } }
- 捕获 then 方法回调函数执行异常
- 如果 then 方法的回调函数内发生异常,或者在then的回调函数内
throw new Error
则需要 reject 传递给下一个 then 方法的错误处理回调函数
try { // 保存成功回调的返回值 let x = successCallback(this.value); // 判断 x 的值是普通值还是 Promise 对象, // 如果是普通值,直接调用 resolve // 如果是 Promise 对象,查看这个 Promise 对象的返回结果, // 再根据返回结果来决定调用 resolve 还是 reject // resolve(x); resolveRromise(promise2, x, resolve, reject); } catch (err) { // 将异常传递给下一个 then 方法的错误处理回调函数, reject(err); }
- 处理 catch 链式调用
前面处理了 resolve 的链式调用情况。而 reject 的处理同理。
...
else if (this.status === REJECTED) {
// failCallback(this.reason);
// 将代码改为异步执行,确保能够在 new Promise 执行完之后拿到 promise2
setTimeout(() => {
try {
// 保存失败回调的返回值
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是 Promise 对象,
// 如果是普通值,直接调用 resolve
// 如果是 Promise 对象,查看这个 Promise 对象的返回结果,
// 再根据返回结果来决定调用 resolve 还是 reject
// resolve(x);
resolveRromise(promise2, x, resolve, reject);
} catch (err) {
// 将异常传递给下一个 then 方法的错误处理回调函数,
reject(err);
}
}, 0);
}
...
!!!注意:
如果在错误处理回调函数中返回了一个正常值(比如: 10000),它就会进入下一个 then 方法的成功处理回调函数。
如果返回的是失败的,它就会进入下一个 then 的错误处理回调函数中
const promise = new MyPromise((resolve, reject) => {
reject(\'失败\');
});
promise
.then(value => {
console.log(value);
return 100;
}, reason => {
console.log(reason); // => 第一个then接收到失败,但是返回了个成功10000
return 10000;
})
.then(value => {
console.log(value); // => 10000
}, reason => {
console.log(reason);
});
- 处理 异步逻辑 的链式调用,并捕获运行异常
...
else {
// 等待
// 保存成功 和 失败的回调函数
// this.successCallback = successCallback;
// this.failCallback = failCallback;
// this.successCallback.push(successCallback);
// this.failCallback.push(failCallback);
// 保存一个函数,这个函数里面执行 成功/失败的回调
this.successCallback.push(() => {
setTimeout(() => {
try {
// 保存成功回调的返回值
let x = successCallback(this.value);
// 判断 x 的值是普通值还是 Promise 对象,
// 如果是普通值,直接调用 resolve
// 如果是 Promise 对象,查看这个 Promise 对象的返回结果,
// 再根据返回结果来决定调用 resolve 还是 reject
// resolve(x);
resolveRromise(promise2, x, resolve, reject);
} catch (err) {
// 将异常传递给下一个 then 方法的错误处理回调函数,
reject(err);
}
}, 0);
});
this.failCallback.push(() => {
setTimeout(() => {
try {
// 保存失败回调的返回值
let x = failCallback(this.reason);
// 判断 x 的值是普通值还是 Promise 对象,
// 如果是普通值,直接调用 resolve
// 如果是 Promise 对象,查看这个 Promise 对象的返回结果,
// 再根据返回结果来决定调用 resolve 还是 reject
// resolve(x);
resolveRromise(promise2, x, resolve, reject);
} catch (err) {
// 将异常传递给下一个 then 方法的错误处理回调函数,
reject(err);
}
}, 0);
});
}
经过上面的处理,再执行回到函数的时候就无需传值了.
...
this.successCallback.shift()();
...
this.failCallback.shift()();
将 then 方法变为可选参数
Promise 的 then 方法的参数是可选的,如果调用 then 方法的时候,不传递参数,这种情况下,Promise 的流程要怎么走呢?
const promise = new MyPromise((resolve, reject) => {
resolve(100);
});
promies
.then()
.then()
.then(value => console.log(value)) // 100
在 then 方法不传递参数的时候,其情况等同于这样的
promise
.then(value => value) // 接收到参数,并将这个参数直接返回
这样的话,promise状态就会传递到下一个 then 方法
then(successCallback, failCallback) {
// 判断是否有传递参数进来
successCallback = successCallback ? successCallback : value => value;
failCallback = failCallback ? failCallback : reason => { throw reason };
...
}
Promise.all 方法
使用 Promise.all 方法可以按照顺序去执行异步任务,并得到顺序的结果
const p1 = function() { return Promise(resolve => { setTimeout(() => { resolve(\'p1\'); }, 2000) }) } const p2 = function() { return new Promise(resolve => { resolve(\'p2\'); }) } Promise.all([\'a\', \'b\', p1(), p2(), \'c\']).then(results => { // [\'a\', \'b\', \'p1\', \'p2\', \'c\'] })
2s 后输出
[\'a\', \'b\', \'p1\', \'p2\', \'c\']
,- Promise.all 方法接收一个数组,内部会按照数组元素的顺序执行,全部执行完之后,再将结果一起返回,返回的结果也是数组,与传入的数组相对应。
- 如果有一个失败了。返回的结果就是失败的。
// 静态方法,可以直接使用 MyPromise.all
// 接收一个数组
static all(array) {
// 定义 result,用来存储结果
const result = [];
// 记录已经执行完毕的数组元素个数
let index = 0;
// 返回一个 promise 对象
return new MyPromise((resolve, reject) => {
// 将数据添加到目标数据中
function addRes(i, value) {
result[i] = value;
// 执行完毕+1
index++;
// 如果数组中所有元素都执行完毕,才 resolve 结果
if (index === array.length) resolve(result);
}
// 遍历数组,并判断每个数组元素是普通值还是 Promise 对象,
// 如果是普通值,直接将元素放到结果集中,
// 如果是Promise 对象,需要获取其返回值,然后放到结果集中
for (let i = 0; i < array.length; i++) {
let current = array[i];
// 判断是否是 promise 对象
if (current instanceof MyPromise) {
current.then(value => addRes(i, value), reason => reject(reason))
} else {
addRes(i, current);
}
}
})
}
Promise.resolve 方法
- Promise.resolve 方法 可以将给定的一个值转化为一个 Promise 对象。
Promise.resolve 对象返回的就是一个 Promise 对象,这个对象包裹着给定的这个值。
Promise.resolve(10).then(value => console.log(value)); // 10
- 如果传入的是一个Promise 对象,它会原封不动的将这个 Promise 对象返回
function p1 () {
return new Promise((resolve, reject) => {
resolve(100)
})
}
Promise.resolve(p1()).then(value => console.log(value)); // 100
// resolve 静态方法
static resolve(value) {
// 如果value 是Promise 对象,直接返回value
if (value instanceof MyPromise) return value;
// 如果是普通值,返回 Promise 对象,并将value 包裹
return new MyPromise(resolve => resolve(value));
}
finally
- Promise 的状态不管成功还是失败的,finally 的回调函数都会被调用
- 非类方法,接收一个回调函数,无论promise对象最终是成功还是失败,finally方法的回调函数始终会被执行一次
- 可以链式调用then,可以拿到当前这个promise对象返回的结果
finally 方法回调函数中 return 可以返回 Promise对象,后续 then 方法的回调函数应该等待 这个 Promise 执行完才能继续进行
finally(callback){ return this.then(value=>{ //then返回一个promise对象 callback() //无论成功失败都会执行 return value; //传递给下一个then },reason=>{ callback() throw reason; }) }
由于callback也可以返回promise,所以用resolve改造一下
注意返回的value和reason是下面例子中promise的value,不是callback中p1这个promise的返回值
finally(callback){
return this.then(value=>{
return MyPromise.resolve(callback()).then(()=>value) //注意value为链式调用的value不是callback方法里的value
},reason=>{
return MyPromise.resolve(callback()).then(()=>{throw reason}) //注意reason
})
}
调用
const promise = new MyPromise((resolve, reject) => {
reject(\'失败\');
});
const p1 = function () {
return new MyPromise(resolve => {
setTimeout(() => {
resolve(\'2s的异步逻辑\');
}, 2000);
})
}
promise.finally(() => {
console.log(\'finally\');
return p1(); // return Promise 对象
}).then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
finally
失败
catch 方法
- catch 方法是用来处理当前 Promise 对象最终状态为失败的情况的。
- 使用它,我们可以不用在 then 方法中传递失败的回调。
只需要在 catch 方法内部去调用 then 方法即可,然后将 then 方法的成功回调传入 undefined,失败的传入一个回调函数
catch(failCallback) {
return this.then(undefined, failCallback);
}
测试
promise.finally(() => {
console.log(\'finally\');
return p1(); // return Promise 对象
}).then(value => {
console.log(value);
}).catch(reason => {
console.log(\'catch\', reason)
})
finally
catch 失败
以上是关于promise内部实现的主要内容,如果未能解决你的问题,请参考以下文章