手把手教学手写Promise

回顾用法

let  promise =  new Promise((resolve, reject) => {
    resolve('成功')
    resolve('reject')
})
promise.then(value => {}, reason => {})
复制代码
  1. Promise 就是一个类 在执行这个类的时候,需要传递一个执行器进去 执行器会立即执行
  2. Promise 有三种状态 分别为成功 fulfilled 、 失败 rejected、 等待 pending
  • 一旦状态确定就不可更改

  • pending -> fulfilled  

  • pending -> rejected  

  1. resolve 和 rejected 函数是用来更改状态的
  • resolve : fulfilled  
  • reject : rejected  
  1. then方法内部做的事情就判断状态 如果状态是成功 调用成功的回调函数 如果状态是失败 调用失败回调函数 then方法是被定义在原型对象中的

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

核心逻辑

定义状态枚举

  • 把三个状态定义为枚举。 便于以后的非字符串输入引起错误

代码如下

const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
复制代码

定义一个类

  • 在回顾用法中知道,new promise 过程中会传入

代码如下

class MyPromise {
  constructor(executor) {
    executor(this.resolve, this.reject);
  }
} 
复制代码

状态更改

  • 状态只允许更改一次。,要做非等待状态的判断
  • 修改对应的状态

代码如下

class MyPromise {
  constructor(executor) {
    executor(this.resolve, this.reject);
  }
  status = PENDING;

  resolve = () => {
    // 状态只允许更改一次。 
    if (this.status !== PENDING) return;
    // 将状态更改为成功
    this.status = RESOLVED;
  };

  reject = () => {
    if (this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
  };
}
复制代码

then

  • 会有成功 、 失败的回调
  • 回调会有原因, 因此在改变状态的时候需要保存原因,在回调的时候返回

代码如下

class MyPromise {
  constructor(executor) {
    executor(this.resolve, this.reject);
  }
  status = PENDING;
  value  = undefined 
  reason = undefined

  resolve = value => {
    if (this.status !== PENDING) return;
    this.status = RESOLVED;
    // 1 保存成功原因
    this.value = value
  };

  reject = reason => {
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    // 1 保存失败原因
    this.reason = reason

  };

  then(successCallback, failCallback) {
    // 2 判断状态
    if(this.status === RESOLVED) {
      successCallback(this.value)
    } else if(this.status === REJECTED){
      failCallback(this.reason)
    }
  }
}
复制代码

异步逻辑

eg: 异步回调。

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("成功");
    reject('失败')
  }, 20000)
});
promise.then(
  (value) => {
    console.log("----value", value);
  },
  (reason) => {
    console.log("----reason", reason);
  }
);
复制代码

此时按照目前的逻辑,是不会执行 successCallbackfailCallback

改造:

  • then 中判断是否是 等待状态 ,如果是,保存 成功和失败 的callback
  • resolvereject中判断 这个回调是否存在,存在并把对应参数带回去调用。

代码如下

class MyPromise {
  constructor(executor) {
    executor(this.resolve, this.reject);
  }
  status = PENDING;
  value = undefined;
  reason = undefined;
	// 1 定义两个callback变量
  successCallback = undefined;
  failCallback = undefined;

  resolve = (value) => {
    if (this.status !== PENDING) return;
    this.status = RESOLVED;
    this.value = value;
    // 2 如果有callback则调用,并把对应参数带过去
    this.successCallback && this.successCallback(this.value)
  };

  reject = (reason) => {
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    this.reason = reason;
    // 2 如果有callback则调用,并把对应参数带过去
    this.failCallback && this.failCallback(this.reason)
  };

  then(successCallback, failCallback) {
    if (this.status === RESOLVED) {
      successCallback(this.value);
    } else if (this.status === REJECTED) {
      failCallback(this.reason);
    } else  {
      // 3 如果还是等待状态,保存callback
      this.successCallback = successCallback;
      this.failCallback = failCallback;
    }
  }
}
复制代码

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

同一个promise对象下面的then方法是可以被调用多次的。

eg:

let promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("成功");
    reject("失败");
  }, 2000);
  // resolve("成功");
  // reject("失败");
});
promise.then(
  (value) => {
    console.log("----value", value);
  },
  (reason) => {
    console.log("----reason", reason);
  }
);

promise.then((value) => {
  console.log(1, value);
});
promise.then((value) => {
  console.log(2, value);
});
promise.then((value) => {
  console.log(3, value);
});
复制代码

那么在此时,目前的代码只会保存最后一次的 callback, 因此我们需要

  • callback 改造成数组

代码如下

class MyPromise {
  constructor(executor) {
    executor(this.resolve, this.reject);
  }
  status = PENDING;
  value = undefined;
  reason = undefined;
	// 1 变成数组
  successCallback = [];
  failCallback = []

  resolve = (value) => {
    if (this.status !== PENDING) return;
    this.status = RESOLVED;
    this.value = value;
    // 2 while 判断长度,shift 不断推出第一个元素,将this.value传进去
    while(this.successCallback.length) this.successCallback.shift()(this.value)
  };

  reject = (reason) => {
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    this.reason = reason;
    // 2 while 判断长度,shift 不断推出第一个元素,将this.value传进去
     while(this.failCallback.length) this.failCallback.shift()(this.reason)
  };

  then(successCallback, failCallback) {
    if (this.status === RESOLVED) {
      successCallback(this.value);
    } else if (this.status === REJECTED) {
      failCallback(this.reason);
    } else  {
      // 3 变成push
      this.successCallback.push(successCallback);
      this.failCallback.push(failCallback);
    }
  }
}
复制代码

链式调用

then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值

普通值

let promise = new Promise((resolve, reject) => {
  resolve("成功");
});

promise.then((value) => {
  console.log(1, value);
  return 100
}).then(value => {
  console.log(value);
})
复制代码

输出结果如下

因此我们需要

  • 返回新的 promise

  • successCallback 返回的东西也需要带给下一个 .then

  • 以前then 需要立即执行,放哪? -> 放入 new Promise 中,立即执行

代码如下

  then(successCallback, failCallback) {
    // 定义为一个新的Promise
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === RESOLVED) {
        // 将当前successCallback return 的值返回
        let x = successCallback(this.value);
        resolve(x)
      } else if (this.status === REJECTED) {
        failCallback(this.reason);
      } else {
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback);
      }
    });
    // 将新的Promise返回,以完成链式调用
    return promise2;
  }
复制代码

callback 返回的又是一个Promise

特俗的, 如果返回的又是一个Promise, 那么我们需要判断,Promise的状态是成功 的还是失败的,如果是成功,则调用resolve, 如果是 失败,则要 reject

需要处理逻辑的如下

  • 判断 x 的值是普通值还是promise对象

  • 如果是普通值 直接调用resolve

  • 如果是promise对象 查看promsie对象返回的结果

  • 再根据promise对象返回的结果 决定调用resolve 还是调用reject

我们定义一个新方法resolvePromise(不是类中的方法,定义在类之外),便于统一调用。 传递的参数分别是 callback的值resolvereject

function resolvePromise (x, resolve, reject) {
   //判断 x 的值是否 promise 对象
  if (x instanceof MyPromise) { 
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}
复制代码

then 中修改如下

  then(successCallback, failCallback) {
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === RESOLVED) {
        let x = successCallback(this.value);
        // 调用resolvePromise
        resolvePromise(x, resolve, reject)
      } else if (this.status === REJECTED) {
        failCallback(this.reason);
      } else {
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback);
      }
    });
    return promise2;
  }
复制代码

特殊循环调用

在特殊情况下,可以让return的promise 为自身,这就会导致循环调用。

eg:

let promise = new Promise((resolve, reject) => {
  resolve("成功");
});
let promise2 = promise
  .then((value) => {
    console.log(1, value);
    return promise2;
  })
复制代码

结果如下

因此我们需要判断 return 的内容是否为 自身。

修改如下

class MyPromise {
  
	....
  
  then(successCallback, failCallback) {
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === RESOLVED) {
        let x = successCallback(this.value);
        // 将promise2 自身传进去做判断
        resolvePromise(promise2, x, resolve, reject)
      } else if (this.status === REJECTED) {
        failCallback(this.reason);
      } else {
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback);
      }
    });
    return promise2;
  }
}

function resolvePromise (promise2, x, resolve, reject) {
  // 加上判断是否为自身, 如果是reject一个错误。
  if(promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
 if (x instanceof MyPromise) { 
   x.then(resolve, reject);
 } else {
   resolve(x);
 }
}
复制代码

但这由有一个新的问题,在resolvePromise传值的时候,promise 是还没有拿到的。 我们需要怎么做呢

  • 处理成异步

因此我们只要加个settime即可。

class MyPromise {
	
 。。。
  
  then(successCallback, failCallback) {
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === RESOLVED) {
        // 添加setTimeout, 使其异步化
        setTimeout(() => {
          let x = successCallback(this.value);
          resolvePromise(promise2, x, resolve, reject);
        }, 0);
      } else if (this.status === REJECTED) {
        failCallback(this.reason);
      } else {
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback);
      }
    });
    return promise2;
  }
}
复制代码

捕获错误

截至至目前,我们都在处理正常的情况,我们需要添加相应的错误兼容,已保证代码的正常执行。

  • 添加try catch

代码如下

class MyPromise {
  constructor(executor) {
    // 添加try catch 
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      // 将错误reject 出去
      this.reject(error)
    }
  }
 
  ....

  then(successCallback, failCallback) {
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === RESOLVED) {
        setTimeout(() => {
          // 添加try catch
          try {
            let x = successCallback(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else if (this.status === REJECTED) {
        failCallback(this.reason);
      } else {
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback);
      }
    });
    return promise2;
  }
}
复制代码

补充其他状态的处理

目前,我们只是处理的resolve 的状态,我们继续添加reject 状态的代码

class MyPromise {

  ...

  then(successCallback, failCallback) {
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === RESOLVED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          // 添加 try catch
          try {
            let x = failCallback(this.reason)
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else {
        this.successCallback.push(successCallback);
        this.failCallback.push(failCallback);
      }
    });
    return promise2;
  }
}
复制代码

异步处理

结合前面成功 和 失败的 处理, 我们还需要对异步的回调函数处理

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject);
    } catch (error) {
      this.reject(error)
    }
  }
  status = PENDING;
  value = undefined;
  reason = undefined;
  successCallback = [];
  failCallback = [];

  resolve = (value) => {
    if (this.status !== PENDING) return;
    this.status = RESOLVED;
    this.value = value;
    // 不再需要传值
    // while (this.successCallback.length) this.successCallback.shift()(this.value);
    while (this.successCallback.length) this.successCallback.shift()();
      
  };

  reject = (reason) => {
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    this.reason = reason;
   // 不再需要传值
    // while (this.failCallback.length) this.failCallback.shift()(this.reason);
    while (this.failCallback.length) this.failCallback.shift()();
  };

  then(successCallback, failCallback) {
    let promise2 = new MyPromise((resolve, reject) => {
      if (this.status === RESOLVED) {
        setTimeout(() => {
          try {
            let x = successCallback(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason)
            resolvePromise(promise2, x, resolve, reject);
          } catch (error) {
            reject(error)
          }
        }, 0);
      } else {
        this.successCallback.push(() => {
          // 将成功的函数copy一份放在此
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error)
            }
          }, 0);
        });
        this.failCallback.push(() => {
          // 将失败的函数copy一份放在此
          setTimeout(() => {
            try {
              let x = failCallback(this.reason)
              resolvePromise(promise2, x, resolve, reject);
            } catch (error) {
              reject(error)
            }
          }, 0);
        });
      }
    });
    return promise2;
  }
}
复制代码

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

我们先来看一段代码

let promise = new Promise((resolve, reject) => {
  resolve("成功");
  // reject("失败");
});

promise
  .then()
  .then()
  .then((value) => {
    console.log(value);
  });
复制代码

then 中,我们可以选择不传递参数,在最后有参数的地方,拿到一层层传递下来的参数。

实际上,上面的代码 等同于 下面的代码

promise
  .then(value => value)
  .then(value => value)
  .then((value) => {
    console.log(value);
  });
复制代码

因此

  • 判断是否有callback, 有则用,没有则补充

    class MyPromise {

    then(successCallback, failCallback) {
    // 添加可选参数
    successCallback = successCallback || (value => value);
    // 失败的回调需加 throw
    failCallback = failCallback || (reason => { throw reason });

    let promise2 = new MyPromise((resolve, reject) => {
      ...
    }
    return promise2;
    复制代码

    }
    }

静态方法

all

Promise 允许我们将多个promise 执行完后一次性返回, 这就是 all 方法。注意的是,这是个静态方法。以下代码是例子。 特殊的,有一个返回的是 reject, 则其他都不会返回

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("成功 p1");
  }, 2000)
});

let p2 = new Promise((resolve, reject) => {
  resolve("成功 p2");
});


let p =  Promise.all(['a1', 'a2', p1, p2, 'c1'])
p.then(res => console.log(res))
复制代码

由以上代码可以得知,

  • all 传递的是个数组
  • 传递元素可以是非promise实例

实现代码

class MyPromise {
  
  ...
  
  // 定义静态方法all
  static all(arr) {
    // 定义一个数组保存结果
    let result = [];
    // 保存一个index 和 数组长度判断是否执行
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        // 保存结果值
        result[key] = value;
        // 每次+1
        index++;
        // 判断是否执行
        if (index === arr.length) {
          resolve(result);
        }
      }

      for (let idx = 0; idx < arr.length; idx++) {
        const current = arr[idx];
        // 判断是否是MyPromise 实例下的
        if (current instanceof MyPromise) {
          // 将then 中的结果放入 结果数组
          current.then(
            (value) => addData(idx, value),
            (reason) => reject(reason)
          );
        } else {
          // 如果不是直接add进结果数组
          addData(idx, current);
        }
      }
    });
  }
}
复制代码

resolve

resolve 可以将传进来的参数变成一个promise. 让后续也可以链式调用。 例子如下

function p1() {
 return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("成功 p1");
    }, 2000);
  });
}

Promise.resolve(10).then((res) => console.log(res));
Promise.resolve(p1()).then((res) => console.log(res));
复制代码

log如下

其实很简单。

  • 判断是否是 Promise 实例,如果是直接 返回

  • 不是 resolve 出去

    class MyPromise {


    // 定义静态方法resolve
    static resolve (value) {
    // 如果是Promise实例,直接返回
    if (value instanceof MyPromise) return value;
    // 不是则resolve 出去
    return new MyPromise(resolve => resolve(value));
    }
    }

finally

我们知道 finally, 无论如何都可以调用 。我们先来复习看下例子是如何用的

let p1 = new Promise((resolve, reject) => {
  console.log('.....');
  resolve("成功 p1");
});

p1.then((res) => console.log(res)).finally(() => {
  console.log("----1");
});
p1.finally(() => {
  console.log("----2");
}).then((res) => console.log(res))
复制代码

log 如下

我们可以发现

  • finally 可以放在如何位置 按顺序执行

  • finally 放前面 后面接 then 是可以拿到 返回的值

  • 需要注意的是,这里有微任务宏任务的概念。 then 后面接 finally 或者 finally 后面接then 都是第二次微任务。

  • 需要考虑finally 在前面,返回的value 是异步的情况。

代码如下

class MyPromise {
  
  ...
  
  finally(callback) {
    // 因为可以链式调用,我们需要返回一个promise, 直接调用.then 方法既可。
    return this.then(value => {
      // 如果 return 的是个promise 需要等待执行完再return 
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }
}
复制代码

catch

话不多说,先看例子

let p1 = new Promise((resolve, reject) => {
  reject('失败回调')
});

p1.then((res) => console.log(res))
.catch(err => {
  console.log(err);
})
复制代码

log 如下;

我们可以发现

  • then 不传递 失败回调时,是可以在 catch 中捕获到的。

其实实现很简单,只需要将callback 放入 then(undefind, callback ) 既可

代码实现如下

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

}
复制代码

结束

自此,已完成全部逻辑。下面是代码总览

const PENDING = 'pending'; // 等待
const FULFILLED = 'fulfilled'; // 成功
const REJECTED = 'rejected'; // 失败

class MyPromise {
  constructor (executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e);
    }
  }
  // promsie 状态 
  status = PENDING;
  // 成功之后的值
  value = undefined;
  // 失败后的原因
  reason = undefined;
  // 成功回调
  successCallback = [];
  // 失败回调
  failCallback = [];

  resolve = value => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为成功
    this.status = FULFILLED;
    // 保存成功之后的值
    this.value = value;
    // 判断成功回调是否存在 如果存在 调用
    while(this.successCallback.length) this.successCallback.shift()()
  }
  
  reject = reason => {
    // 如果状态不是等待 阻止程序向下执行
    if (this.status !== PENDING) return;
    // 将状态更改为失败
    this.status = REJECTED;
    // 保存失败后的原因
    this.reason = reason;
    // 判断失败回调是否存在 如果存在 调用
    while(this.failCallback.length) this.failCallback.shift()()
  }
  
  then (successCallback, failCallback) {
     // 添加可选参数
    successCallback = successCallback ||  (value => value);
    // 失败的回调需加 throw
    failCallback = failCallback ||  (reason => { throw reason });
    
    let promsie2 = new MyPromise((resolve, reject) => {
      // 判断状态
      if (this.status === FULFILLED) {
        setTimeout(() => {
          // 添加容错
          try {
            let x = successCallback(this.value);
            resolvePromise(promsie2, x, resolve, reject)
          }catch (e) {
            reject(e);
          }
        }, 0)
      }else if (this.status === REJECTED) {
        setTimeout(() => {
          // 添加容错
          try {
            let x = failCallback(this.reason);
            resolvePromise(promsie2, x, resolve, reject)
          }catch (e) {
            reject(e);
          }
        }, 0)
      } else {
        // 等待
        // 将成功回调和失败回调存储起来
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value);
              resolvePromise(promsie2, x, resolve, reject)
            }catch (e) {
              reject(e);
            }
          }, 0)
        });
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason);
              resolvePromise(promsie2, x, resolve, reject)
            }catch (e) {
              reject(e);
            }
          }, 0)
        });
      }
    });
    return promsie2;
  }

  finally(callback) {
    // 因为可以链式调用,我们需要返回一个promise, 直接调用.then 方法既可。
    return this.then(value => {
      // 如果 return 的是个promise 需要等待执行完再return 
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }

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

  // 定义静态方法all
  static all(arr) {
    // 定义一个数组保存结果
    let result = [];
    // 保存一个index 和 数组长度判断是否执行
    let index = 0;
    return new MyPromise((resolve, reject) => {
      function addData(key, value) {
        // 保存结果值
        result[key] = value;
        // 每次+1
        index++;
        // 判断是否执行
        if (index === arr.length) {
          resolve(result);
        }
      }

      for (let idx = 0; idx < arr.length; idx++) {
        const current = arr[idx];
        // 判断是否是MyPromise 实例下的
        if (current instanceof MyPromise) {
          // 将then 中的结果放入 结果数组
          current.then(
            (value) => addData(idx, value),
            (reason) => reject(reason)
          );
        } else {
          // 如果不是直接add进结果数组
          addData(idx, current);
        }
      }
    });
  }

  // 定义静态方法resolve
  static resolve (value) {
    // 如果是Promise实例,直接返回
    if (value instanceof MyPromise) return value;
    // 不是则resolve 出去
    return new MyPromise(resolve => resolve(value));
  }
}


// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve 
// 如果是promise对象 查看promsie对象返回的结果 
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
function resolvePromise (promsie2, x, resolve, reject) {
  if (promsie2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }
  if (x instanceof MyPromise) {
    // promise 对象
    // x.then(value => resolve(value), reason => reject(reason));
    x.then(resolve, reject);
  } else {
    // 普通值
    resolve(x);
  }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享