Handwritten Promise static method

Handwritten promise

Front background: handwritten promise in vernacular

resolve、reject

As a method called directly by the class, it is naturally a static method.
**The principle of resolve and reject is to instantiate a promise internally, and then call resolve or reject. **The essence is no different from instantiating the promise call yourself.

class LcPromise {
    
    
  constructor(executed) {
    
    }
  then(onFulfilled, onRejected) {
    
    }
	catch(onRejected) {
    
    }
  finally(fn) {
    
    }

  static resolve(value) {
    
    
    return new LcPromise((resolve, reject) => resolve(value))
  }

  static reject(reason) {
    
    
    return new LcPromise((resolve, reject) => reject(reason))
  }
}

all、allSettled

Usage of all method: Receive an array of promise instances, wait for all the promises in the array to be fulfilled, then call then's onFulfilled, as long as there is one promise rejected, then call then's onRejected immediately.

Promise.all([]).then(onFulfilled).catch(onRejected)

From this we can see that all must return a promise instance, otherwise how to call then. We can wait for the results of the promise instances in the array one by one. When all the promise instances execute onFulfilled, it means that the whole all is already in the fulfilled state, and the promise instances returned by resolve all. If one of them executes onRejected, it means that there is an error in all, and the entire promise instance of all can be rejected.
The key to the whole problem is when the promise returned by all executes resolve and when executes reject.

static all(promiseArr) {
    
    
  const allResult = [];
  return new LcPromise((resolve, reject) => {
    
    
    promiseArr.forEach((promise) => {
    
    
      // 执行每个 promise 实例
      promise.then(
        (res) => {
    
    
          // 收集每个 promise 的成功结果
          allResult.push(res);
          // 当收集完所有的 promise,说明整体的 promise 应该为 fulfilled 状态了,执行 resolve
          if (allResult.length === promiseArr.length) resolve(allResult);
        },
        (err) => {
    
    
          // 只要有一个 promise rejected 了,就执行整个 promise 的 reject 函数
          reject(err);
        }
      );
    });
  });
}

allSettled is similar to all, the difference is that when there is a rejected promise in the promise array received by allSettled, it will not interrupt the execution of the entire allSettled function, and the promise returned by allSettled will not become rejected, it will still be pending, and continue to execute the subsequent ones in the array promise. It means that the promise returned by allSettled will not execute the reject function, and it must be in the fulfilled state at the end. For promises in the rejected state in the array, the return value of allSettled is an array object. The object contains the state and result or error message of each promise.

static allSettled(promiseArr) {
    
    
  const allResult = [];
  return new LcPromise((resolve, reject) => {
    
    
    promiseArr.forEach((promise) => {
    
    
      promise.then(
        (res) => {
    
    
          allResult.push({
    
     status: "fulfilled", res: res });
          if (allResult.length === promiseArr.length) resolve(allResult);
        },
        (err) => {
    
    
          allResult.push({
    
     status: "rejected", err: err });
          if (allResult.length === promiseArr.length) resolve(allResult);
        }
      );
    });
  });
}

test:

const p1 = new LcPromise((resolve, reject) => {
    
    
  resolve(1111);
});
const p2 = new LcPromise((resolve, reject) => {
    
    
  // resolve(2222);
  reject("hhh");
});
const p3 = new LcPromise((resolve, reject) => {
    
    
  resolve(3333);
});

LcPromise.all([p1, p2, p3])
  .then((res) => console.log(res))
  .catch((err) => console.log(err));

// hhh

LcPromise.allSettled([p1, p2, p3])
  .then((res) => console.log(res))
  .catch((err) => console.log(err));

// [
//   { status: 'fulfilled', res: 1111 },
//   { status: 'rejected', err: 'hhh' },
//   { status: 'fulfilled', res: 3333 }
// ]

race、any

Usage of the race method: basically similar to all, the difference is that as long as there is a promise in the array that confirms the state, the state of the promise returned by the entire race is immediately determined.
So if a promise executes the then method, the resolve method or reject method of the overall promise can be executed immediately.

static race(promiseArr) {
    
    
  return new LcPromise((resolve, reject) => {
    
    
    promiseArr.forEach((promise) => {
    
    
      // 有一个 promise 确定了状态,就立即确定了整体 promise 的状态
      promise.then(res => {
    
    
        resolve(res)
      }, err => {
    
    
        reject(err)
      })
    })
  })
}

The usage of any is to execute resolve as long as one promise in the array is fulfilled, and the overall promise is fulfilled. The reject function is executed only when all promises in the array are rejected, the whole is rejected, and a merged error array containing all error messages is returned new AggregateError(arr).
Note that this constructor is not implemented in nodejs and can only be tested in the browser. It has an errors attribute, you can view error messages.

static any(promiseArr) {
    
    
  return new LcPromise((resolve, reject) => {
    
    
    const reasonArr = []
    promiseArr.forEach((promise) => {
    
    
      promise.then(res => {
    
    
        resolve(res)
      }, err => {
    
    
        reasonArr.push(err)
        if (promiseArr.length === reasonArr.length) reject(new AggregateError(reasonArr))
      })
    })
  })
}

test:

const p1 = new LcPromise((resolve, reject) => {
    
    
  setTimeout(() => {
    
    
    resolve(1111);
  }, 3000);
});
const p2 = new LcPromise((resolve, reject) => {
    
    
  // resolve(2222);
  setTimeout(() => {
    
    
    reject("hhh");
  }, 1000);
});
const p3 = new LcPromise((resolve, reject) => {
    
    
  setTimeout(() => {
    
    
    resolve(3333);
  }, 2000);
});

LcPromise.race([p1, p2, p3])
  .then((res) => console.log(res))
  .catch((err) => console.log(err));

// hhh 打印最快确定状态的信息
// 所有 promise rejected 的情况
const p1 = new LcPromise((resolve, reject) => {
    
    
  setTimeout(() => {
    
    
    // resolve(1111);
    reject("h1");
  }, 3000);
});
const p2 = new LcPromise((resolve, reject) => {
    
    
  // resolve(2222);
  setTimeout(() => {
    
    
    reject("h2");
  }, 1000);
});
const p3 = new LcPromise((resolve, reject) => {
    
    
  setTimeout(() => {
    
    
    // resolve(3333);
    reject("h3");
  }, 2000);
});

LcPromise.any([p1, p2, p3])
  .then((res) => console.log(res))
  .catch((err) => console.log(err.errors));

// (3) ['h2', 'h3', 'h1']

full promise

const PROMISE_STATUS_PENDING = "pending";
const PROMISE_STATUS_FULFILLED = "fulfilled";
const PROMISE_STATUS_REJECTED = "rejected";

const execFunctionWithCatchError = (execFn, value, resolve, reject) => {
    
    
  try {
    
    
    const res = execFn(value);
    resolve(res);
  } catch (error) {
    
    
    reject(error);
  }
};

class LcPromise {
    
    
  constructor(executed) {
    
    
    this.status = PROMISE_STATUS_PENDING;

    this.value = undefined;
    this.reason = undefined;

    this.onFulfilledFns = [];
    this.onRejectedFns = [];

    const resolve = (value) => {
    
    
      this.value = value;
      queueMicrotask(() => {
    
    
        if (this.status != PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_FULFILLED;
        this.onFulfilledFns.forEach((fns) => fns(this.value));
      });
    };
    const reject = (reason) => {
    
    
      this.reason = reason;
      queueMicrotask(() => {
    
    
        if (this.status != PROMISE_STATUS_PENDING) return;
        this.status = PROMISE_STATUS_REJECTED;
        this.onRejectedFns.forEach((fns) => fns(this.reason));
      });
    };

    executed(resolve, reject);
  }

  then(onFulfilled, onRejected) {
    
    
    onFulfilled = onFulfilled || ((value) => value);
    onRejected =
      onRejected ||
      ((err) => {
    
    
        throw err;
      });

    return new LcPromise((resolve, reject) => {
    
    
      // 在 resolve reject 中执行
      if (onFulfilled)
        this.onFulfilledFns.push(() => {
    
    
          execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
        });
      if (onRejected)
        this.onRejectedFns.push(() => {
    
    
          execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
        });

      // 在自身中执行
      if (this.status === PROMISE_STATUS_FULFILLED) {
    
    
        execFunctionWithCatchError(onFulfilled, this.value, resolve, reject);
      }
      if (this.status === PROMISE_STATUS_REJECTED) {
    
    
        execFunctionWithCatchError(onRejected, this.reason, resolve, reject);
      }
    });
  }

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

  finally(fn) {
    
    
    this.then(fn, fn);
  }

  static resolve(value) {
    
    
    return new LcPromise((resolve, reject) => resolve(value));
  }

  static reject(reason) {
    
    
    return new LcPromise((resolve, reject) => reject(reason));
  }

  static all(promiseArr) {
    
    
    const allResult = [];
    return new LcPromise((resolve, reject) => {
    
    
      promiseArr.forEach((promise) => {
    
    
        // 执行每个 promise 实例
        promise.then(
          (res) => {
    
    
            // 收集每个 promise 的成功结果
            allResult.push(res);
            // 当收集完所有的 promise,说明整体的 promise 应该为 fulfilled 状态了,执行 resolve
            if (allResult.length === promiseArr.length) resolve(allResult);
          },
          (err) => {
    
    
            // 只要有一个 promise rejected 了,就执行整个 promise 的 reject 函数
            reject(err);
          }
        );
      });
    });
  }

  static allSettled(promiseArr) {
    
    
    const allResult = [];
    return new LcPromise((resolve, reject) => {
    
    
      promiseArr.forEach((promise) => {
    
    
        promise.then(
          (res) => {
    
    
            allResult.push({
    
     status: "fulfilled", res: res });
            if (allResult.length === promiseArr.length) resolve(allResult);
          },
          (err) => {
    
    
            allResult.push({
    
     status: "rejected", err: err });
            if (allResult.length === promiseArr.length) resolve(allResult);
          }
        );
      });
    });
  }

  static race(promiseArr) {
    
    
    return new LcPromise((resolve, reject) => {
    
    
      promiseArr.forEach((promise) => {
    
    
        promise.then(
          (res) => {
    
    
            resolve(res);
          },
          (err) => {
    
    
            reject(err);
          }
        );
      });
    });
  }

  static any(promiseArr) {
    
    
    return new LcPromise((resolve, reject) => {
    
    
      const reasonArr = [];
      promiseArr.forEach((promise) => {
    
    
        promise.then(
          (res) => {
    
    
            resolve(res);
          },
          (err) => {
    
    
            reasonArr.push(err);
            if (promiseArr.length === reasonArr.length)
              reject(new AggregateError(reasonArr));
          }
        );
      });
    });
  }
}

Guess you like

Origin blog.csdn.net/qq_43220213/article/details/129775183