Promise手写,含各种方法

41 阅读4分钟

1、先来个普通版的Promise手写吧~

const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'

class MyPromise {
  constructor(executor) {
    this.status = Pending;
    this.value = undefined;
    this.reason = undefined;
    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    let resolve = (value) => {
      if (this.status === Pending) {
        this.status = Fulfilled;
        this.value = value;
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };

    let reject = (reason) => {
      if (this.status === Pending) {
        this.status = Rejected;
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };

    try {
      executor(resolve, reject);
    } catch (error) {
      reject(error);
    }
  }

  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
    onRejected =
      typeof onRejected === "function"
        ? onRejected
        : (err) => {
            throw err;
          };

    let promise2 = new Promise((resolve, reject) => {
      if (this.status === Fulfilled) {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.status === Rejected) {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      }

      if (this.status === Pending) {
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });

        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });

    return promise2;
  }
  
  catch(errCallback) {
    return this.then(null, errCallback);
  }
}

const resolvePromise = (promise2, x, resolve, reject) => {
  if (promise2 === x) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<Promise>")
    );
  }
  let called;
  if ((typeof x === "object" && x != null) || typeof x === "function") {
    try {
      let then = x.then;
      if (typeof then === "function") {
        then.call(
          x,
          (y) => {
            if (called) return;
            called = true;
            resolvePromise(promise2, y, resolve, reject);
          },
          (r) => {
            if (called) return;
            called = true;
            reject(r);
          }
        );
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
};

测试一下MyPromise:

let promise = new MyPromise((resolve, reject) => {
  resolve("成功");
})
  .then()
  .then()
  .then((data) => {
      console.log(data);
    },
    (err) => {
      console.log("err", err);
    }
  )
// 成功

2、添加finally方法:

class MyPromise {
  constructor(executor) {
    ...
  }

  then(onFulfilled, onRejected) {
    ...
  }

  catch(errCallback) {
    ...
  }
  
  finally(fn) {
    return this.then(
      (value) => {
        return MyPromise.resolve(fn()).then(() => value);
      },
      (err) => {
        return MyPromise.reject(fn()).then(() => {
          throw err;
        });
      }
    );
  }
}

测试finally方法:

MyPromise.resolve(3).finally(()=>{
 return new MyPromise((resolve,reject)=>{
   setTimeout(() => {
       resolve(123)
   }, 3000);
 })
}).then(data=>{
 console.log(data,'success')
}).catch(err=>{
 console.log(err,'error')
})

// 3 success

3、添加Mypromise.all方法:

class MyPromise {
  constructor(executor) {
    ...
  }

  then(onFulfilled, onRejected) {
    ...
  }

  catch(errCallback) {
    ...
  }
  
  finally(fn) {
    ...
  }
  
  //all方法-所有的promise的状态变成fulfilled,包装实例才会变成fulfilled, 有一个变成rejected,包装实例就会变成rejected
  static all(promises) {
    if (!promises.length) return MyPromise.resolve([]);

    promises = promises.map((item) =>
      item instanceof MyPromise ? item : MyPromise.resolve(item)
    );

    return new MyPromise((resolve, reject) => {
      let result = [];
      let count = 0;

      for (let i = 0; i < promises.length; i++) {
        promises[i]
          .then((val) => {
            result[i] = val;
            count++;
            if (count === promises.length) {
              resolve(result);
            }
          })
          .catch((e) => {
            reject(e);
          });
      }
    });
  }
  
  static resolve(value) {
    return new MyPromise((resolve, reject) => {
      resolve(value);
    });
  }
  static reject(reason) {
    return new MyPromise((resolve, reject) => {
      reject(reason);
    });
  }
}

测试MyPromise.all方法:

let p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("ok1");
  }, 1000);
});
let p2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("ok2");
  }, 2000);
});
MyPromise.all([1,2,p1,p2]).then(
  (data) => {
    console.log("mypromise resolve", data);
  },
  (err) => {
    console.log("mypromise reject", err);
  }
);
Promise.all([1,2,p1,p2]).then(
 (data) => {
   console.log("promise resolve", data);
 },
 (err) => {
   console.log("promise reject", err);
 }
);

// promise resolve [ 1, 2, 'ok1', 'ok2' ]
// mypromise resolve [ 1, 2, 'ok1', 'ok2' ]

4、添加MyPromise.allSettled方法:

class MyPromise {
  constructor(executor) {
    ...
  }

  then(onFulfilled, onRejected) {
    ...
  }

  catch(errCallback) {
    ...
  }
  
  finally(fn) {
    ...
  }

  static all(promises) {
    ...
  }
  //allSettled方法-所有状态改变,不管是fulfilled还是rejected, 包装实例都会变成fulfilled
  static allSettled(promises) {
    if (!promises.length) {
      throw Error("不能为空");
    }
    promises = promises.map((item) =>
      item instanceof MyPromise ? item : MyPromise.resolve(item)
    );

    return new MyPromise((resolve, reject) => {
      let result = [];
      let count = promises.length;

      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (val) => {
            result[i] = {
              status: Fulfilled,
              value: val,
            };
            count -= 1;
            if (count === 0) {
              resolve(result);
            }
          },
          (err) => {
            result[i] = {
              status: Rejected,
              reason: err,
            };
            count -= 1;
            if (count === 0) {
              resolve(result);
            }
          }
        );
      }
    });
  }
}

测试MyPromise.allSSettled方法:

let p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("ok1");
  }, 1000);
});
let p2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject("ok2");
  }, 2000);
});
MyPromise.allSettled([1,2,p1,p2]).then((res) => {
  console.log('mypromise',res);
})
Promise.allSettled([1,2,p1,p2]).then((res) => {
 console.log('promise',res);
})

// promise [
//  { status: 'fulfilled', value: 1 },
//  { status: 'fulfilled', value: 2 },
//  { status: 'fulfilled', value: 'ok1' },
//  { status: 'rejected', reason: 'ok2' }
//]
// mypromise [
//  { status: 'Fulfilled', value: 1 },
//  { status: 'Fulfilled', value: 2 },
//  { status: 'Fulfilled', value: 'ok1' },
//  { status: 'Rejected', reason: 'ok2' }
//]

5、添加MyPromise.race方法:

class MyPromise {
  constructor(executor) {
    ...
  }

  then(onFulfilled, onRejected) {
    ...
  }

  catch(errCallback) {
    ...
  }
  finally(fn) {
    ...
  }

  static all(promises) {
    ...
  }
  
  static allSettled(promises) {
    ...
  }
  
  // race方法-其中一个状态改变,不论成功或者失败,最终状态会由最先改变状态的那个promise的状态决定
  static race(promises) {
    promises.map((item) =>
      item instanceof MyPromise ? item : MyPromise.resolve(item)
    );

    return new MyPromise((resolve, reject) => {
      for (let i = 0; i < promises.length; i++) {
        promises[i].then(resolve, reject);
      }
    });
  }
}

测试MyPromise.race方法:

let p1 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("ok1");
  }, 1000);
});
let p2 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject("ok2");
  }, 800);
});
MyPromise.race([p1, p2]).then(res=>{
 console.log('myPromise----',res);
}).catch(err=>{
 console.log('myPromise----',err);
})
Promise.race([p1, p2]).then(res=>{
 console.log('promise--',res);
}).catch(err=>{
 console.log('promise---',err);
})
// promise--- ok2
// myPromise---- ok2

6、添加MyPromise.any方法:

class MyPromise {
  constructor(executor) {
    ...
  }

  then(onFulfilled, onRejected) {
    ...
  }

  catch(errCallback) {
    ...
  }
  finally(fn) {
    ...
  }
 
  static all(promises) {
    ...
  }
  
  static allSettled(promises) {
    ...
  }
  
  static race(promises) {
    ...
  }
  
  // any方法,有一个变成fulfilled状态,包装实例就会变成fulfilled状态,当所有的状态变成rejected,包装实例才会变成rejected
  static any(promises) {
    return new MyPromise((resolve, reject) => {
      let count = promises.length;
      let errs = [];

      if (count === 0) reject("all promises rejected");

      promises = promises.map((item) =>
        item instanceof MyPromise ? item : MyPromise.resolve(item)
      );

      for (let i = 0; i < promises.length; i++) {
        promises[i].then(
          (res) => {
            resolve(res);
          },
          (err) => {
            errs[i] = err;
            count -= 1;
            if (count === 0) {
              reject(errs);
            }
          }
        );
      }
    });
  }
}

测试MyPromise.any方法:

let p3 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("ok1");
  }, 1000);
});
let p4 = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    reject("ok2");
  }, 1000);
});
MyPromise.any([1,2,p3,p4])
  .then((res) => {
    console.log("mypromise 成功", res);
  })
  .catch((err) => {
    console.log("mypromise 失败", err);
  });
Promise.any([1,2,p3,p4])
.then((res) => {
  console.log("promise 成功1", res);
})
.catch((err) => {
  console.log("promise 失败1", err);
});

// promise 成功1 1
// mypromise 成功 1

7、Promise不会中断,如果想要Promise执行过程中中断,可以利用race方法包装一下

function wrap(promise) {
  let abort;
  let newPromise = new MyPromise((resolve, reject) => {
    abort = reject;
  });
  let p = MyPromise.race([promise, newPromise]);
  p.abort = abort;
  return p;
}

let promise = new MyPromise((resolve, reject) => {
  setTimeout(() => {
    resolve("成功的");
  }, 3000);
});

let newPromise = wrap(promise);
setTimeout(() => {
  newPromise.abort("超时了");
}, 1000);

newPromise
  .then((res) => {
    console.log("成功---", res);
  })
  .catch((err) => {
    console.log("失败---", err);
  });
  
// 失败--- 超时了

8、将node 的API转换成一个接收相同的方法并且可以和then结合使用的工具函数:

import fs from "fs";
// util.promisify() 的简化实现。不包括所有情况,不要在 prod 环境中使用此选项!
function promisify(fn) {
  return function (args) {
    return new Promise((resolve, reject) => {
      fn.apply(this, [].concat(args).concat([(err, res) => {
            if (err != null) {
              return reject(err);
            }
            resolve(res);
          }
        ])
      );
    });
  };
}
// 将 fs.readFile() 转换为一个接受相同参数但返回 Promise 的函数。
const readFile = promisify(fs.readFile);
// 现在可以将 readFile() 与 then 一起使用!
readFile("./package.json").then(buf=>{
 const obj = JSON.parse(buf.toString("utf8"));
 console.log(obj.name);
})

// handwritten-code