手写promise过程

1 MyPromise核心逻辑实现

  1. promise是个构造函数

  2. 传入一个可执行函数。函数的入参第一个为 fullFill函数,第二个为 reject函数; 函数立即执行, 参数函数异步执行

  3. 状态一旦更改就不可以变更 只能 pending => fulfilled 或者 pending => rejected

  4. then方法内部做的事情就判断状态,如果状态是成功,调用成功的回调函数,如果状态是失败,调用失败的函数

  5. then成功回调有一个参数,表示成功之后的值,then失败回调有一个参数,表示失败后的原因

    let p = new Promise((resolve, reject) => {
        resolve('成功');  // 状态改为成功
        // reject('失败');  // 状态改为失败
    })
    p.then(value => {
        // 成功的值
        console.log(value) // '成功'
    }, reason => {
        // 失败的原因
        console.log(reason) // 失败
    })
    

    promise基本核心代码

    const PENDING = 'pending';
    const FULFILLED = 'fulfilled';
    const REJECT = 'reject';
    class MyPromise {
      constructor(executor) {
        // 立即执行
        executor(this.resolve, this.reject);
      }
      status = PENDING; // 状态
      value = undefined; // 成功的值
      reason = undefined; // 失败的原因
      
      resolve = (value) => {
        if(this.status !== PENDING) return;   //状态改变过后不能再更改
        this.status = FULFILLED; // 状态改为成功
        this.value = value; // 保存成功的值
      }
      
      reject = (reason) => {
        if(this.status !== PENDING) return;
        this.status = REJECT; // 状态改为失败
        this.reason = reason; // 保存失败的原因
      }
      then(successCallback, failCallback) {
        if(this.status === FULFILLED) { // 状态为成功,执行成功回调
          successCallback(this.value);
        } else if(this.status === REJECT) { // 状态为失败,执行失败回调
          failCallback(this.reason);
        }
      }
    }
    

2 在promise中加入异步逻辑

刚刚在实现promise时,是没有考虑异步情况的

let p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
})
p.then(value => {
    // 成功的值
    console.log(value) // '成功'
}, reason => {
    // 失败的原因
    console.log(reason) // 失败
})

异步代码不会阻塞主线程代码的执行,马上会执行then方法,但此时then中的回调函数都不会执行,因为两秒后才通过resolve将状态改为成功,所以此时promise的状态为‘pending’。为了在两2s后能执行promise中的异步代码,应该在then中加入异步逻辑处理,也就是在状态为’pending’时进行处理。

then方法中将成功回调和失败回调都存储起来,然后在resolve和reject中,分别执行存储的成功回调和异步回调

class MyPromise {
    ...
    // 定义存储的回调
    successCallback = undefined;  // 成功回调
    failCallback = undefined; // 失败回调
	...
    // then方法中将成功和失败回调存储起来
    then(successCallback, failCallback) {
        if(this.status === FULFILLED) { // 状态为成功,执行成功回调
          successCallback(this.value);
        } else if(this.status === REJECT) { // 状态为失败,执行失败回调
          failCallback(this.reason);
        } else {
          // 当前状态为pending
          // 将成功回调和失败回调存储起来
          this.successCallback = successCallback;
          this.failCallback = failCallback;
        }
  	}
	...
    // resolve方法中执行存储的成功回调
    resolve = (value) => {
		...
        this.successCallback && this.successCallback(this.value);
        ...
    }
    // reject方法中执行存储的失败回调
    reject = (reason) => {
		...
        this.failCallback && this.failCallback(this.reason);
        ...
    }
}

3 实现then方法多次调用添加多个处理函数

当promise中有异步任务时,有多个then的情况时,最后一个then会覆盖前面的then

let p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
})
p.then(value => {
    console.log(value)
})
p.then(value => {
    console.log(value)
})
p.then(value => {
    console.log(value) // '成功'
})
// 打印一次 '成功'

只执行了一次console.log,这与实际promise不符,所以此时应该在状态为’pending’时将多个then的回调函数都存储起来,然后在resolve和reject方法中将存储的回调任务依次执行

class MyPromise {
    ...
    // 定义存储的回调
    successCallback = [];  // 成功回调数组
    failCallback = []; // 失败回调数组
	...
    // then方法中将成功和失败回调存储起来
    then(successCallback, failCallback) {
        if(this.status === FULFILLED) { // 状态为成功,执行成功回调
          successCallback(this.value);
        } else if(this.status === REJECT) { // 状态为失败,执行失败回调
          failCallback(this.reason);
        } else {
          // 当前状态为pending
          // 将所有成功回调和失败回调存储起来
          this.successCallback.push(successCallback);
          this.failCallback.push(failCallback);
        }
  	}
	...
    // resolve方法中执行存储的成功回调
    resolve = (value) => {
		...
        while(this.successCallback.length) {
            this.successCallback.shift()(this.value);
        }
        ...
    }
    // reject方法中执行存储的失败回调
    reject = (reason) => {
		...
        while(this.failCallback.length) {
            this.failCallback.shift()(this.reason);
        }
        ...
    }
}

此时有多个then的情况时,每一个then中的回调函数都会被正确执行

let p = new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
})
p.then(value => {
    console.log(value)
})
p.then(value => {
    console.log(value)
})
p.then(value => {
    console.log(value) // '成功'
})
// 打印三次 '成功'

4 实现then方法的链式调用

promise/A+规范规定,then方法可以链式调用

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
})
p.then(value => {
    console.log(value); // 成功
    return 100;
}).then(value => {
    console.log(value); // 100
})

实现链式调用,可以在then方法中创建promise对象,然后返回这个promise对象

then(successCallback, failCallback) {
    let promise1 = new MyPromise(() => {
        if(this.status === FULFILLED) { // 状态为成功,执行成功回调
            successCallback(this.value);
        } else if(this.status === REJECT) { // 状态为失败,执行失败回调
            failCallback(this.reason);
        } else {
            // 当前状态为pending
            // 将成功回调和失败回调存储起来
            this.successCallback.push(successCallback);
            this.failCallback.push(failCallback);
        }
    });
    return promise1;
}

如何把上一个then方法中回调函数的返回值传递给下一个then方法中的回调函数,可以将回调函数执行的返回值,通过新创建的Promise的resolve传递给下一个then

then(successCallback, failCallback) {
    let promise1 = new MyPromise((resolve, reject) => {
        if(this.status === FULFILLED) { // 状态为成功,执行成功回调
            let x = successCallback(this.value);
            resolve(x);
        } else if(this.status === REJECT) { // 状态为失败,执行失败回调
            failCallback(this.reason);
        } else {
            // 当前状态为pending
            // 将成功回调和失败回调存储起来
            this.successCallback.push(successCallback);
            this.failCallback.push(failCallback);
        }
    });
    return promise1;
}

promise/A+规范规定,then方法可以返回一个普通值,也可以返回一个新的promise对象

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')
    }, 2000)
})
p.then(value => {
    console.log(value); // 成功
    return 100; // 返回普通值
}).then(value => {
    // 返回一个Promise对象
    return new Promise(resolve => {
		resolve(200)
    })
}).then(value => {
    
})

如果返回的是一个普通值,可以像前面处理一样,直接resolve这个普通值;如果返回的是一个Promise对象,就需要查看这个对象的状态,根据Promise对象返回的结果决定调用resolve还是调用reject

由于在多个地方需要判断这个过程,所有封装成一个函数

class MyPromise {
  ...
  then(successCallback, failCallback) {
    let promise1 = new MyPromise((resolve, reject) => {
      if(this.status === FULFILLED) { // 状态为成功,执行成功回调
        let x = successCallback(this.value);
        // 判断x的值是普通值还是promise对象
        // 如果是普通值,直接调用resolve
        // 如果是promise对象,查看promise对象返回的结果
        // 再根据promise对象返回的结果,决定调用resolve还是调用reject
        resolvePromise(x, resolve, reject) // 解析返回的结果
      } else if(this.status === REJECT) { // 状态为失败,执行失败回调
        failCallback(this.reason);
      } else {
        // 当前状态为pending
        // 将成功回调和失败回调存储起来
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback);
      }
    });
    return promise1;
  }
  ...
}

function resolvePromise(x, resolve, reject) {
  if(x instanceof MyPromise) {
    // promise对象
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

then方法的回调函数可以返回promise对象,但是有一种是例外的,在then方法的回调函数中不能返回当前的then方法它所返回的promise对象,否则会发生promise对象的循环调用,程序会报错

let promise = new Promise(resolve => {
  resolve(100)
});
let p1 = promise.then(value => {
  console.log(value)
  return p1;
})

此时会报错: Uncaught (in promise) TypeError: Chaining cycle detected for promise #

let promise = new Promise(resolve => {
  resolve(100)
});
let p1 = promise.then(value => {
  console.log(value)
  return p1;
})
p1.then(()=>{}, (reason)=>{
    console.log(reason.message)
})

此时promise不会报错,会拿到这个错误信息

所以我们也需要判断出自己返回自己的情况。如果自己返回自己,就走reject

resolvePromise函数传递参数时,把返回的promise对象也传进来进行判断

class MyPromise {
  ...
  then(successCallback, failCallback) {
    let promise1 = new MyPromise((resolve, reject) => {
      if(this.status === FULFILLED) { // 状态为成功,执行成功回调
         queueMicrotask(()=>{
            let x = successCallback(this.value);
            // 判断x的值是普通值还是promise对象
            // 如果是普通值,直接调用resolve
            // 如果是promise对象,查看promise对象返回的结果
            // 再根据promise对象返回的结果,决定调用resolve还是调用reject
            resolvePromise(promise1, x, resolve, reject) // 解析返回的结果          
         })
      } else if(this.status === REJECT) { // 状态为失败,执行失败回调
         queueMicrotask(()=>{
            let x = failCallback(this.reason);
            resolvePromise(promise1, x, resolve, reject) // 解析返回的结果          
         })
      } else {
          // 当前状态为pending
          // 将成功回调和失败回调存储起来
          this.successCallback.push(successCallback);
          this.failCallback.push(failCallback);
      }
    });
    return promise1;
  }
  ...
}

function resolvePromise(promise1, x, resolve, reject) {
  if(promise1 === x) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  if(x instanceof MyPromise) {
    // promise对象
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}

敲黑板:这里需要注意,调用resolvePromise函数时需要传递promise对象promise1,但是此时MyPromise还没有实例完成,拿不到promise1,此时怎么办呢?将内部的处理逻辑放到异步队列中,等MyPromise实例化完成后,再执行内部的代码,此时promise1对象就能成功获取。

let promise = new MyPromise(resolve => {
  resolve(100)
});
let p1 = promise.then(value => {
  console.log(value)
  return p1;
})
p1.then(()=>{}, (reason)=>{
    console.log(reason.message)
})

5 捕获错误

promise中有错误时,会传递给then方法的第二个回调函数

let promise = new Promise((resolve, reject) => {
    throw new Error('错误信息')
}).then(()=>{}, (reason) => {
    console.log(reason) // 错误信息
})
  1. 在执行器里捕获错误

    class MyPromise {
      constructor(executor) {
        // 立即执行
        try {
          executor(this.resolve, this.reject);
        } catch(e) {
          this.reject(e);
        }
      }
      ...
    }
    
  2. 在then中捕获错误,状态为成功,失败,等待中时都应该处理

then(successCallback, failCallback) {
    let promise1 = new MyPromise((resolve, reject) => {
        if(this.status === FULFILLED) { // 状态为成功,执行成功回调
            queueMicrotask(()=>{
                try {
                    let x = successCallback(this.value);
                    // 判断x的值是普通值还是promise对象
                    // 如果是普通值,直接调用resolve
                    // 如果是promise对象,查看promise对象返回的结果
                    // 再根据promise对象返回的结果,决定调用resolve还是调用reject
                    resolvePromise(promise1, x, resolve, reject) // 解析返回的结果
                } catch(e) {
                    reject(e);
                }
            })
        } else if(this.status === REJECT) { // 状态为失败,执行失败回调
            queueMicrotask(() => {
                try {
                    let x = failCallback(this.reason);
                    resolvePromise(promise1, x, resolve, reject)
                } catch(e) {
                    reject(e);
                }
            })
        } else {
            // 当前状态为pending
            // 将成功回调和失败回调存储起来
            this.successCallback.push(() => {
                queueMicrotask(() => {
                    try {
                        let x = successCallback(this.value);
                        resolvePromise(promise1, x, resolve, reject)
                    } catch(e) {
                        reject(e);
                    }
                })
            });
            this.failCallback.push(() => {
                queueMicrotask(() => {
                    try {
                        let x = failCallback(this.reason);
                        resolvePromise(promise1, x, resolve, reject)
                    } catch(e) {
                        reject(e);
                    }
                })
            });
        }
    });
    return promise1;
}

至此,promise的核心逻辑基本上已经完成了

6 将then方法的参数变成可选参数

promise/A+规范中,then方法没有传任何参数的话,等同于默认传递前面then或promise中的状态

let promise = new Promise((resolve, reject) => {
  resolve(100);
});
promise.then().then().then(value => console.log(value))
// 等同于
let promise = new Promise((resolve, reject) => {
  resolve(100);
});
promise.then(value => value)
    .then(value => value)
    .then(value => console.log(value))

所以在then方法内部要对参数先进行判断是否存在

successCallback ? successCallback : value => value;
failCallback ? failCallback : reason => { throw reason };

7 Promise.all方法实现

function p1() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('p1')
    }, 2000)
  })
}
function p2() {
  return new Promise((resolve, reject) => {
    resolve('p2')
  })
}
Promise.all('a', 'b', p1(), p2(), 'c').then(result => {
  // result => ['a', 'b', 'p1', 'p2', 'c']
})

可以看出,Promise.all接受一个数组参数,数组元素可以是普通值,也可以是promise对象,Promise.all返回一个promise对象,成功状态传递过来的值是参数数组resolve的顺序结果数组,实现如下:

static all(array) {
    let result = []; // 结果数组
    // 添加数据的公共方法
    function addData(key, value) {
        result[key] = value;
    }
    // 返回MyPromise对象
    return new MyPromise((resolve, reject) => {
        for(let i = 0; i < array.length; i++) {
            let current = array[i];
            if(current instanceof MyPromise) {
                // MyPromise对象,成功的值添加到结果数组中,失败的话,调用reject
                current.then((value) => {addData(i, value)}, (reason) = {reject(reason))}
            } else {
                // 普通值
                addData(i, array[i]); 
            }
        } 
        resolve(result)
    })
}

测试一下:

function p1() {
  return new MyPromise((resolve, reject) => {
    setTimeout(() => {
      resolve('p1')
    }, 2000)
  })
}
function p2() {
  return new MyPromise((resolve, reject) => {
    resolve('p2')
  })
}
MyPromise.all('a', 'b', p1(), p2(), 'c').then(result => {
  // result => [ 'a', 'b', <1 empty item>, 'p2', 'c' ]
})

原因,p1中有异步代码,在all中for循环时,遍历到p1时,由于p1是MyPromise对象,且里面有异步代码,此时状态为‘pending’,所以then中的代码不会执行,即不会将结果添加到结果数组

解决办法,每添加一次数据,就计数一次,该计数值和数组长度相等时,就可以resolve了

  static all(array) {
    let result = []; // 结果数组
    let index = 0;
    // 返回MyPromise对象
    return new MyPromise((resolve, reject) => {
      // 添加数据的公共方法
      function addData(key, value) {
        result[key] = value;
        index++;
        if(index === array.length) {
          resolve(result);
        }
      }
      for(let i = 0; i < array.length; i++) {
        let current = array[i];
        if(current instanceof MyPromise) {
          // MyPromise对象,成功的值添加到结果数组中,失败的话,调用reject
          current.then((value) => {addData(i, value)}, (reason) => {reject(reason)})
        } else { 
          // 普通值
          addData(i, array[i]);  
        }
      }
      // resolve(result)
    })
  }

8 Promise.resolve

将给定的值转换成promise对象

Promise.resolve(10).then(value => {console.log(value)})
Promise.resolve(p1).then(value => {console.log(value)})

resolve方法参数如果是promise对象,直接返回该对象,如果不是promise对象,将该值包裹成promise对象然后返回

  static resolve(value) {
    if(value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value))
  }

9 finally方法

特性:

  1. 无论当前promise状态是成功还是失败,finally方法都会被执行
  2. finally方法仍然可以链式调用then方法
  finally(callback) {
    return this.then((value) => {
      callback();
      return value;
    }, (reason) => {
      callback();
      throw reason;
    })
  }

测试:

function p2() {
  return new MyPromise((resolve, reject) => {
    resolve('p2')
    // reject('p2') 
  })
}
p2().finally(() => {
  console.log('finally')
  return 'p2'
}).then(value => {
  console.log(value)
}, reason => {
  console.log(reason)
})

此时可以看出,无论promise状态是成功还是失败,都会执行finally方法

但是,会有一个问题,

function p1() {
  return new MyPromise((resolve, reject) => {
    setTimeout(()=> {
      resolve('p1')
    }, 2000)
  })
}
function p2() {
  return new MyPromise((resolve, reject) => {
    resolve('p2') 
  })
}
p2().finally(() => {
  console.log('finally')
  return p1();
}).then(value => {
  console.log(value)
}, reason => { 
  console.log(reason)
})

then中的方法应该等p1计时器完成后再执行,但是这里会立即执行,所以改变如下:

finally(callback) {
    return this.then((value) => {
        return MyPromise.resolve(callback()).then(() => value);
    }, (reason) => {
        return MyPromise.resolve(callback()).then(() => {throw reason});
    })
}

10 catch方法

catch(failCallback) {
    return this.then(undefined, failCallback);
}

最后,整个手写过程如下:

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECT = 'reject';
class MyPromise {
  constructor(executor) {
    // 立即执行
    try {
      executor(this.resolve, this.reject);
    } catch(e) {
      this.reject(e);
    }
  }
  status = PENDING; // 状态
  value = undefined; // 成功的值
  reason = undefined; // 失败的原因
  successCallback = [];  // 成功回调
  failCallback = []; // 失败回调
  
  resolve = (value) => {
    if(this.status !== PENDING) return;   //状态改变过后不能再更改
    this.status = FULFILLED; // 状态改为成功
    this.value = value; // 保存成功的值
    // this.successCallback && this.successCallback(this.value);
    while(this.successCallback.length) {
      this.successCallback.shift()();
    }
  }
  
  reject = (reason) => {
    if(this.status !== PENDING) return;
    this.status = REJECT; // 状态改为失败
    this.reason = reason; // 保存失败的原因
    // this.failCallback && this.failCallback(this.reason);
    while(this.failCallback.length) {
      this.failCallback.shift()();
    }
  }
  then(successCallback, failCallback) {
    successCallback ? successCallback : value => value;
    failCallback ? failCallback : reason => { throw reason };
    let promise1 = new MyPromise((resolve, reject) => {
      if(this.status === FULFILLED) { // 状态为成功,执行成功回调
        queueMicrotask(()=>{
          try {
            let x = successCallback(this.value);
            // 判断x的值是普通值还是promise对象
            // 如果是普通值,直接调用resolve
            // 如果是promise对象,查看promise对象返回的结果
            // 再根据promise对象返回的结果,决定调用resolve还是调用reject
            resolvePromise(promise1, x, resolve, reject) // 解析返回的结果
          } catch(e) {
            reject(e);
          }
        })
      } else if(this.status === REJECT) { // 状态为失败,执行失败回调
        queueMicrotask(() => {
          try {
            let x = failCallback(this.reason);
            resolvePromise(promise1, x, resolve, reject)
          } catch(e) {
            reject(e);
          }
        })
      } else {
        // 当前状态为pending
        // 将成功回调和失败回调存储起来
        this.successCallback.push(() => {
          queueMicrotask(() => {
            try {
              let x = successCallback(this.value);
              resolvePromise(promise1, x, resolve, reject)
            } catch(e) {
              reject(e);
            }
          })
        });
        this.failCallback.push(() => {
          queueMicrotask(() => {
            try {
              let x = failCallback(this.reason);
              resolvePromise(promise1, x, resolve, reject)
            } catch(e) {
              reject(e);
            }
          })
        });
        return;
      }
    });
    return promise1;
  }
  finally(callback) {
    return this.then((value) => {
      return MyPromise.resolve(callback()).then(() => value);
    }, (reason) => {
      return MyPromise.resolve(callback()).then(() => {throw reason});
    })
  }
  catch(failCallback) {
    return this.then(undefined, failCallback);
  }
  static all(array) {
    let result = []; // 结果数组
    let index = 0;
    // 返回MyPromise对象
    return new MyPromise((resolve, reject) => {
      // 添加数据的公共方法
      function addData(key, value) {
        result[key] = value;
        index++;
        if(index === array.length) {
          resolve(result);
        }
      }
      for(let i = 0; i < array.length; i++) {
        let current = array[i];
        if(current instanceof MyPromise) {
          // MyPromise对象,成功的值添加到结果数组中,失败的话,调用reject
          current.then((value) => {addData(i, value)}, (reason) => {reject(reason)})
        } else { 
          // 普通值
          addData(i, array[i]);  
        }
      }
      // resolve(result)
    })
  }
  static resolve(value) {
    if(value instanceof MyPromise) return value;
    return new MyPromise(resolve => resolve(value))
  }
}

function resolvePromise(promise1, x, resolve, reject) {
  if(promise1 === x) {
    // 如果自己返回自己,就报错,并且return后,不用判断后面的逻辑
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if(x instanceof MyPromise) {
    // promise对象
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
} 

以上是主要的核心逻辑,还有些边界情况可能没有考虑

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值