Promise 实现详解

这篇文章通过自己实现一个Promise来加深对promise的理解,具体如何实现符合promise规范的代码,可以查看Promises/A+

梳理 Promise 功能

1、Promise是一个构造函数,它接收一个执行函数作为参数,执行函数里面包含resolvereject两个方法;

2、调用resolve方法表示成功,reject方法表示失败,结果会在实例的then函数中以参数函数的方式拿到;

3、then是实例方法,参数包含成功的回调函数和失败的回调函数,只用resolvereject执行后方法才会执行;

状态变化

promise里面的状态决定了当前能执行什么方法,先定义三个变量来保存状态:


const PENDING_STATUS = "pending"; // 等待,用户还没调用resolve或者reject

const FULFILLED_STATUS = "fulfilled"; // 成功,用户调用了resolve

const REJECTED_STATUS = "rejected"; // 失败,用户调用了reject

有状态就会有出现状态的原因了,后面还需要把这些原因传给then方法,我们先创建构造函数:


function Promise(executor) {

this.status = PENDING_STATUS;

this.value = null; // 保存resolve方法的参数

this.reason = null;// 保存reject方法的参数

}

定义好了状态我们还要知道什么时候去修改它,我们知道executor执行函数接受resolvereject两个方法,这两个方法执行的时候就是我们修改状态的时候了:


function Promise(executor) {

this.status = PENDING_STATUS;

this.value = null; // 保存resolve方法的参数

this.reason = null;// 保存reject方法的参数

const resolve = (value)=>{

// 如果状态不是 pending 返回

if (this.status !== PENDING_STATUS) return;

// 先保存参数

this.value = value;

// 改变状态

this.status = FULFILLED_STATUS;

}

const reject = (reason)=>{

if (this.status !== PENDING_STATUS) return;

this.reason = reason;

this.status = REJECTED_STATUS;

}

try {

executor(resolve, reject)

} catch (error) {

reject(error)

}

}

then 方法

我们知道then方法接受两个参数,成功后的函数回调和失败后的函数回调,那么我们可以在函数里面判断当前状态,并执行对应的方法:


Promise.prototype.then = function (onfulfilled, onrejected){

if (this.status === FULFILLED_STATUS){}

if (this.status === REJECTED_STATUS){}

if (this.status === PENDING_STATUS){}

}

根据状态去判断执行onfulfilled还是onrejected方法比较容易,但是如果状态仍处于 pending,就不能直接执行onfulfilledonrejected了,我们可以利用发布订阅的方式将方法缓存起来,直到resolvereject被调用才取出来执行,我们先在构造函数里面定义onResolvedCallbackonRejectedCallback两个数组:


function Promise(executor) {

...

this.onResolvedCallback = [];

this.onRejectedCallback = [];

...

}

then方法里面,我们在pending状态时先将执行函数保存起来:


Promise.prototype.then = function (onfulfilled, onrejected){

if (this.status === FULFILLED_STATUS){

onfulfilled(this.value);

}

if (this.status === REJECTED_STATUS){

onrejected(this.reason);

}

if (this.status === PENDING_STATUS){

this.onResolvedCallback.push(()=>{

onfulfilled(this.value);

})

this.onRejectedCallback.push(()=>{

onrejected(this.reason);

})

}

}

resolvereject函数中取出并执行:


const resolve = (value)=>{

if (this.status !== PENDING_STATUS) return;

this.value = value;

this.status = FULFILLED_STATUS;

// 取出缓存的方法并执行

this.onResolvedCallback.forEach(fn=>fn());

}

链式调用

promise方法可以实现链式调用,上一个函数onfulfilled的返回结果,可以在下一次then函数中被获取:


new Promise((resolve)=>resolve("result1"))

.then(r=>`result2`)

.then(r=>`result3`)

.then(r=>`result4`);

可以看到then方法可以不断的被链式调用,这说明then方法也返回了一个promise实例:


Promise.prototype.then = function (onfulfilled, onrejected){

let promise = new Promise((resolve,reject)=>{

if (this.status === FULFILLED_STATUS) {

onfulfilled(this.value);

}

if (this.status === REJECTED_STATUS) {

onrejected(this.reason);

}

if (this.status === PENDING_STATUS) {

this.onResolvedCallback.push(() => {

onfulfilled(this.value);

})

this.onRejectedCallback.push(() => {

onrejected(this.reason);

})

}

});

return promise;

}

但是onfulfilledonrejected的返回结果,有可能是普通的值或者是promise,所以我们也要处理下返回的值:


let promise = new Promise((resolve,reject)=>{

if(this.status === FULFILLED_STATUS){

let value = onfulfilled(this.value);

// 函数在setTimeout里面执行,这样能保证拿到返回的promise变量

setTimeout(()=>{

try {

resolvePromise(promise,value,resolve,reject);

} catch (error) {

onrejected(error)

}

})

}

if(this.status === REJECTED_STATUS){

let value = onrejected(this.reason);

setTimeout(() => resolvePromise(promise, value, resolve, reject));

}

if(this.status === PENDING_STATUS){

this.onResolvedCallback.push(()=>{

let value = onfulfilled(this.value);

setTimeout(() => {

try {

resolvePromise(promise, value, resolve, reject);

} catch (error) {

onrejected(error)

}

})

});

this.onRejectedCallback.push(()=>{

let value = onrejected(this.reason);

setTimeout(() => resolvePromise(promise, value, resolve, reject));

})

}

});

resolvePromise函数我们要做的处理是判断返回的value值,这里有几个判断需要注意:

  • value 跟返回的 promise 相同,循环引用直接抛出错误
  • value 是 Promise 实例,在 then 方法里面执行 resolve
  • 直接resolve value

function resolvePromise(promise,value,resolve,reject){

if (promise === value) throw new Error("循环引用");

if(value instanceof Promise){

let then = value.then;

try {

then.call(value,(result)=>{

resolve(result);

},(error)=>{

reject(error);

})

} catch (error) {

reject(error)

}

}else{

resolve(value);

}

}

值的穿透

Promise支持一种比较奇葩的写法:


let p = new Promise((resolve)=>resolve("result"));

p.then().then().then(result=>console.log(result));

遇到这种写法,需要在then函数里面提前做判断:


Promise.prototype.then = function(onfulfilled,onrejected){

onfulfilled = typeof onfulfilled === "function" ? onfulfilled : (val)=>val;

onrejected = typeof onrejected === "function" ? onrejected : (error)=>error;

...

}

catch


Promise.prototype.catch = function (errorCallback) {

return this.then(null, errorCallback)

}

resolve 和 reject


Promise.resolve = function (value) {

return new Promise((resolve) => resolve(value))

}

Promise.reject = function (value) {

return new Promise((resolve, reject) => reject(value))

}

Promise.all

all方法接受一个数组,我们需要判断数组里面的Promise方法,只有在then方法执行后才算完成,可以定义一个完成变量保存完成的数量,当完成数量等于数组长度时,才resolve最终的结果


Promise.all = function (values) {

return new Promise((resolve, reject) => {

let result = [];

let resultIndex = 0; // 执行完成的数量

let len = values.length;

function processData(index, value) {

result[index] = value;

resultIndex++; // 执行完成后加一

if (resultIndex === len) {

// 最终结果

resolve(result);

}

}

for (let i = 0; i < len; i++) {

let value = values[i];

let then = value.then;

if (then && (typeof then === "function")) {

then.call(value, result => {

processData(i, result)

}, reject => {

processData(i, reject)

})

} else {

processData(i, value)

}

}

})

}

race

race执行返回最快完成的结果,所以当then函数执行后直接执行resolve返回结果:


Promise.race = function (values) {

return new Promise((resolve, reject) => {

for (let i = 0; i < values.length; i++) {

let value = values[i];

let then = value.then;

if (then && (typeof then === "function")) {

then.call(value, result => {

resolve(result)

}, reject)

} else {

resolve(value);

break;

}

}

})

}

你可能感兴趣的:(javascript,前端,promise)