Ways to Write Promises by hand.

Abstract

Promise is an asynchronous solution for the JS community, providing developers with basic methods such as. Then (), promise.resolve (), promise.reject (). In addition, methods such as.all() and.race() are provided to make it easier to combine and control multiple Promise instances.

This article will implement a more advanced method manually on the basic method of Promise to deepen the understanding of Promise:

  • 🤔 ️ implementationPromise.all
  • 🤔 ️ implementationPromise.race
  • 🤔 ️ implementationPromise.any
  • 🤔 ️ implementationPromise.allSettled
  • 🤔 ️ implementationPromise.finally

⚠️ Full code and use cases can be found at github.com/dongyuanxin… .

Realize the Promise. All

process

Promise.all(iterators) returns a new Promise instance. Iterators contain multiple promise instances passed in from the outside world.

For a new Promise instance returned, there are two cases:

  • If the state of all incoming Promise instances becomesfulfilled, the state of the returned Promise instance isfulfilled, and its value is an array of all the promise values passed in.
  • If one of the Promise instances changes staterejected, the state of the returned Promise instance immediately changes torejected.

Code implementation

Implementation idea:

  • It doesn’t have to be an array object, it can be a traverser.
  • Each instance passed in doesn’t have to be a promisePromise.resolve()packaging
  • With “counters”, flag whether all instances are in statefulfilled
Promise.myAll = function(iterators) {
  const promises = Array.from(iterators);
  const num = promises.length;
  const resolvedList = new Array(num);
  let resolvedNum = 0;

  return new Promise((resolve, reject) = > {
    promises.forEach((promise, index) = > {
      Promise.resolve(promise)
        .then(value= > {
          // Save the value of the Promise instance
          resolvedList[index] = value;
          // Through the counter, mark whether all instances are fulfilled
          if (++resolvedNum === num) {
            resolve(resolvedList);
          }
        })
        .catch(reject);
    });
  });
};
Copy the code

Realize the Promise. Race

process

Promise.race(iterators) passes the same parameters and returns the same values as promise.all. However, the state and value of the returned promise instance completely depend on: Among all the promise instances passed in, the one that changes the state first (whether this is a pity or rejected).

Code implementation

Implementation idea:

  • An incoming instancepending -> fulfilled, its value isPromise.raceValue of the returned Promise instance
  • An incoming instancepending -> rejected, its error isPromise.raceError for the returned Promise instance
Promise.myRace = function(iterators) {
  const promises = Array.from(iterators);

  return new Promise((resolve, reject) = > {
    promises.forEach((promise, index) = > {
      Promise.resolve(promise)
        .then(resolve)
        .catch(reject);
    });
  });
};
Copy the code

Realize the Promise. Any

I write the front-end focused tech blog Xin-tan.com. You can access the Watch or Star articles repository at github.com/dongyuanxin… Or follow the public account “Xintan Blog” to receive the latest news of articles.

process

Promise.any(iterators) passes the same parameters and returns the same values as promise.all.

If any of the passed instances becomes a big pity, the state of the returned Promise instance will immediately become a big pity. If all instances change to Rejected, then it returns a Promise instance with the state Rejected.

⚠️ promise. all = promise. any = array.prototype. every = array.prototype. some

Code implementation

The implementation idea is similar to promise.all. This counter is used to indicate whether all instances of rejected are rejected because the processing logic for asynchronous processes is different.

Promise.any = function(iterators) {
  const promises = Array.from(iterators);
  const num = promises.length;
  const rejectedList = new Array(num);
  let rejectedNum = 0;

  return new Promise((resolve, reject) = > {
    promises.forEach((promise, index) = > {
      Promise.resolve(promise)
        .then(value= > resolve(value))
        .catch(error= > {
          rejectedList[index] = error;
          if(++rejectedNum === num) { reject(rejectedList); }}); }); }); };Copy the code

Realize the Promise. AllSettled

process

Promise.allsettled (iterators) passes the same parameters and returns the same values as Promise.all.

According to ES2020, the state of this returned Promise instance can only be fulfilled. For all incoming Promise instances, it waits for each promise instance to end and returns the specified data format.

If you pass in two promise instances A and B, a changes to Rejected and the error is error1. This will be a pity, and value is 1. Then promise.allSettled returns the value of the Promise instance:

[{ status: "rejected".value: error1 }, { status: "fulfilled".value: 1 }];
Copy the code

Code implementation

Counters in the implementation that count all incoming promise instances.

const formatSettledResult = (success, value) = >
  success
    ? { status: "fulfilled", value }
    : { status: "rejected".reason: value };

Promise.allSettled = function(iterators) {
  const promises = Array.from(iterators);
  const num = promises.length;
  const settledList = new Array(num);
  let settledNum = 0;

  return new Promise(resolve= > {
    promises.forEach((promise, index) = > {
      Promise.resolve(promise)
        .then(value= > {
          settledList[index] = formatSettledResult(true, value);
          if (++settledNum === num) {
            resolve(settledList);
          }
        })
        .catch(error= > {
          settledList[index] = formatSettledResult(false, error);
          if(++settledNum === num) { resolve(settledList); }}); }); }); };Copy the code

Promise.all, promise. any, and promise. allSettled

These three methods all use counters for asynchronous process control. The following table compares the use of counters in different methods to enhance understanding:

The method name use
Promise.all Mark the number of depressing instances
Promise.any Indicates the number of instances of Rejected
Promise.allSettled Mark the number of all instances (depressing and Rejected)

Realize the Promise. Prototype. Finally

process

It is a syntactic sugar that fires after the current Promise instance executes either a THEN or a catch.

For example, a promise prints time stamps in both then and catch:

new Promise(resolve= > {
  setTimeout((a)= > resolve(1), 1000);
})
  .then(value= > console.log(Date.now()))
  .catch(error= > console.log(Date.now()));
Copy the code

Now the common logic that must be executed in finally can be abbreviated as: finally

new Promise(resolve= > {
  setTimeout((a)= > resolve(1), 1000);
}).finally((a)= > console.log(Date.now()));
Copy the code

As you can see, Promise. Prototype. Finally the execution has nothing to do with Promise instance state, does not depend on Promise after the execution of the returned result value. The argument passed in is a function object.

Code implementation

Implementation idea:

  • Given that the Promise’s resolver may be an asynchronous function, the finally implementation adds callback logic by calling the then method on the instance
  • Value transparently transmitted successfully, error transparently transmitted failed
Promise.prototype.finally = function(cb) {
  return this.then(
    value= > Promise.resolve(cb()).then((a)= > value),
    error =>
      Promise.resolve(cb()).then((a)= > {
        throwerror; })); };Copy the code

Refer to the link

  • The code and use cases are at: github.com/dongyuanxin…
  • ECMAScript 6 Getting Started -Promise Objects
  • Github.com/tc39/propos…
  • Github.com/matthew-and…

If you feel you have gained something, welcome to Watch or Star articles warehouse github.com/dongyuanxin… , or scan the public account “Xintan Blog” to unlock more articles.