preface

Small bag the front of the first QQ number is 444, with a long time, feel to see 4 this number really pleasing to the eye.

All, Promise.allSettled, Promise.race, promise.any, accept the same type of arguments but handle them logically differently. How different is Promise? Don’t worry, wait for the bag to come slowly.

At the beginning of the article, Xiao Bao put forward several questions for us:

  • Promise.allPromise.allSettledWhat’s the difference?
  • Promise.raceHow does that work?Promise.anyNow, what’s the difference?
  • Can the four brothers only accept arrays as arguments?
  • How should we elegantly and perfectly implement the four Brothers method?

Promise.all

Promise.all in the current handwritten topic heat frequency should be top5 level, so we need to deeply grasp the promise. all method. Let’s start with a brief review of the All method.

Based on learning

Promise. All method is similar to a group of brothers walking side by side, parameters can be analogous to a group of brothers, only when all brothers are happy, all eldest will harvest happiness; As long as one brother is unhappy, the eldest is unhappy.

The promise.all () method is used to wrap multiple Promise instances into a new Promise instance.

const p = Promise.all([p1, p2, p3]);
Copy the code

The promise. all method takes an array of arguments, and p1, p2, and p3 are all Promise instances. If it is not a Promise instance, the promise.resolve method is first called to convert the parameter to a Promise instance before the next step is taken.

The status of return value P is determined by P1, P2 and P3, which can be divided into two cases:

  • Only when the states of P1, P2 and P3 become depressing, the state of P will become depressing. At this time, the return values of P1, P2 and P3 will form an array and be passed to the callback function of P.

  • If p1, P2, and P3 are rejected, P becomes rejected, and the return value of the first rejected instance is passed to p’s callback function.

// Emulate asynchronous promises
const p1 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    resolve(1);
  }, 1000);
});
/ / promise
const p2 = Promise.resolve(2);
/ / constant value
const p3 = 3;
// Failed promise
const p4 = Promise.reject("error");
// Asynchronous failed promise
const p5 = new Promise((resolve, reject) = > {
  setTimeout(() = > {
    reject("TypeError");
  }, 1000);
});

// 1
Promise.all([p1, p2, p3])
  .then((data) = > console.log(data)) / / [1, 2, 3]
  .catch((error) = > console.log(error));
  
// 2. There are failed promises
Promise.all([p1, p2, p3, p4])
  .then((data) = > console.log(data))
  .catch((error) = > console.log(error)); // error

// 3. Multiple failed promises exist
Promise.all([p1, p2, p3, p4, p5])
  .then((data) = > console.log(data))
  .catch((error) = > console.log(error)); // error
Copy the code

From the output of the above case, we can draw the following conclusions:

  • pThe status is determined by the execution result of the parameters. Success is returned if all the parameters are successful, and failure occurs if there is one failure
  • Parameters for thePromiseInstance, will passPromise.resolveConverted intoPromiseThe instance
  • Success returns an array with the data sorted by parameters
  • Short circuit effect: only the first failure message is returned

Iterator interface parameters

The ES6 Primer also points out that the promise. all method may not be an array, but it must have an Iterator interface and that each member returned is a Promise instance

Promise. All uses an Iterator type that requires iterators to be all Promise instances. Let’s use String as an example to see if promise. all can support iterating items as non-Promise instances.

// ['x', 'i', 'a', 'o', 'b', 'a', 'o']
Promise.all("xiaobao").then((data) = > console.log(data));
Copy the code

As you can see, promises treat the Iterator type the same way arrays do; if the argument is not a Promise instance, promise. all is called first to convert it to a Promise instance.

Thought analysis

  1. Promise.allWill return a new onePromiseobject
Promise.all = function (promises) {
  return new Promise((resolve, reject) = > {});
};
Copy the code
  1. (Bright spot) allMethod arguments can be arrays, or they can beIteratorType, and therefore should be usedfor ofLoop through.
Promise.all = function (promises) {
  return new Promise((resolve, reject) = > {
    for (let p of promises) {
    }
  });
};
Copy the code
  1. Some parameters may not bePromiseType, so the parameter is passed before being usedPromise.resolveconversion
Promise.all = function (promises) {
  return new Promise((resolve, reject) = > {
    for (let p of promises) {
      // Make sure all parameters are Promise instances, and then proceed
      Promise.resolve(p).then((data) = > {
        / /...}); }}); };Copy the code
  1. IteratorType we have no way of knowing the iteration depth, so we maintain onecountUsed to recordpromiseTotal while maintainingfulfilledCountStands for accomplishedpromiseNumber, whencount === fulfilledCountRepresents all incomingPromiseIf the command is executed successfully, data is displayed.
Promise.all = function (promises) {
  let count = 0; / / the total number of promise
  let fulfilledCount = 0; // The number of completed promises
  return new Promise((resolve, reject) = > {
    for (let p of promises) {
      count++; // Promise totals + 1
      Promise.resolve(p).then((data) = > {
        fulfilledCount++; // The number of fulfilled promises +1
        if (count === fulfilledCount) {
          // the last promise is completedresolve(); }}); }}); };Copy the code

Count === = all promises are fulfilled.

The promise. then method is microTasks, and the Event Loop will execute the microTasks after the synchronization task is completed. Count++ is in the sync code section, so you have successfully counted the total number of promises before executing the promise.then method.

Then the promise.then method is executed in turn, and ledcount is incremented. When count === ledCount is enabled, all promises have been successfully completed.

  1. The order of return data should beallThe more difficult part of the method.
  • Create an arrayresultStore allpromiseSuccess data
  • infor ofLoop, useletVariable definitionsi, whose value is equal to the current traversal index
  • letThe variable defined does not get variable promotion, so we just letresult[i]promiseSuccess data, so that you can output the results in the order of parameter input
Promise.all = function (promises) {
  const result = []; // Store promise success data
  let count = 0;
  let fulfilledCount = 0;
  return new Promise((resolve, reject) = > {
    for (let p of promises) {
      // I is the number of promises iterated
      // Use let to avoid closure issues
      let i = count;
      count++;
      // Make sure all parameters are Promise instances, and then proceed
      Promise.resolve(p).then((data) = > {
        fulfilledCount++;
        // Assign the ith promise success data to the corresponding location
        result[i] = data;
        if (count === fulfilledCount) {
          // the last promise is completed
          // Returns the result arrayresolve(result); }}); }}); };Copy the code
  1. Let’s do the boundary case
    • apromiseFailure – direct callrejectCan be
    • The incomingpromiseThe number of0— Return an empty array (specification)
    • Code execution throws an exception — returns an error message
// Redundant code is omitted
Promise.all = function (promises) {
    return new Promise((resolve, reject) = > {
        // 3. Catch exceptions in code execution
        try{
            for (let p of promises) {
                Promise.resolve(p).then(data= > {}
                                .catch(reject);  Call reject directly to return the failure cause})}// 2. The number of incoming promises is 0
            if (count === 0) {
                resolve(result)
            }
        } catch(error) {
            reject(error)
        }
    })
}
Copy the code

The source code to achieve

Let’s put the above code together, add detailed comments, and test the success of handwritten promise.all.

Promise.all = function (promises) {
  const result = []; // Store promise success data
  let count = 0; / / the total number of promise
  let fulfilledCount = 0; // Fulfill the promise number
  return new Promise((resolve, reject) = > {
    // Catch exceptions in code execution
    try {
      for (let p of promises) {
        // I is the number of promises iterated
        // Use let to avoid closure issues
        let i = count;
        count++; // Promise totals + 1
        Promise.resolve(p)
          .then((data) = > {
            fulfilledCount++; // The number of fulfilled promises +1
            // Assign the ith promise success data to the corresponding location
            result[i] = data;
            if (count === fulfilledCount) {
              // the last promise is completed
              // Returns the result array
              resolve(result);
            }
          })
          .catch(reject);
        // The number of incoming promises is 0
        if (count === 0) {
          resolve(result); // Returns an empty array}}}catch(error) { reject(error); }}); };Copy the code

Test code (use the test code in the case, with the Iterator type Stirng attached):

// 1
Promise.all([p1, p2, p3])
  .then((data) = > console.log(data)) / / [1, 2, 3]
  .catch((error) = > console.log(error));

// 2. There are failed promises
Promise.all([p1, p2, p3, p4])
  .then((data) = > console.log(data))
  .catch((error) = > console.log(error)); // error

// 3. Multiple failed promises exist

Promise.all([p1, p2, p3, p4, p5])
  .then((data) = > console.log(data))
  .catch((error) = > console.log(error)); // error

// 4. String type
Promise.all("zcxiaobao").then((data) = > console.log(data));
// ['z', 'c', 'x', 'i', 'a', 'o', 'b', 'a', 'o']
Copy the code

Promise.allSettled

Based on learning

Not every group of brothers will end up with the best boss (allSettled method). He doesn’t care if brothers die or live. He just wants to see if they do it, and his job is to get all the results back.

The promise.allSettled () method takes an array as an argument, each member of which is a Promise object, and returns a new Promise object. The state of the returned Promise object will change only after all the Promise objects in the parameter array have state changes (whether this is a pity or Rejected).

Using the above example, let’s see how this differs from the promise.all method.

// 1
Promise.allSettled([p1, p2, p3])
  .then((data) = > console.log(data)) / / [1, 2, 3]
  .catch((error) = > console.log(error));
  
// 2. There are failed promises
Promise.allSettled([p1, p2, p3, p4])
  .then((data) = > console.log(data))
  .catch((error) = > console.log(error)); // error

// 3. Multiple failed promises exist
Promise.allSettled([p1, p2, p3, p4, p5])
  .then((data) = > console.log(data))
  .catch((error) = > console.log(error)); // error

// 4. Pass the String type
Promise.allSettled("zc").then((data) = > console.log(data));
Copy the code

From the output results, we can find:

  1. allSettledMethods will only succeed, not fail
  2. Return result Each member is an object with a fixed format
    • ifpromiseSuccess, object property valuestatus: fulfilled.valueRecord success value
    • If the promise fails, the object property valuestatus: rejected.reasonRecord the failure cause.
  3. allSettledThe method is also acceptableIteratorType parameters

Thought analysis

The biggest differences between the allSettled method and the ALL method are two:

  1. allSettledMethods do not fail
  2. allSettledMethod returns have a fixed format

We can adapt the ALL method around these two points.

The All method is settled by counting successes. The allSettled method doesn’t count successes and failures, so we need to count the total number of successes/failures.

In the process of adding up the total size, construct allSettled data format on a case-to-case basis: push in success format, push in failure format.

The source code to achieve

Because of the handwritten basis of the ALL method, the above is not a long-winded implementation.

Promise.allSettled = function (promises) {
  const result = [];
  let count = 0;
  let totalCount = 0; // Fulfill the promise number
  return new Promise((resolve, reject) = > {
    try {
      for (let p of promises) {
        let i = count;
        count++; // Promise totals + 1
        Promise.resolve(p)
          .then((res) = > {
            totalCount++;
            // Returns success format data on success
            result[i] = {
              status: "fulfilled".value: res,
            };
            // The execution is complete
            if (count === totalCount) {
              resolve(result);
            }
          })
          .catch((error) = > {
            totalCount++;
            // Return failure format data on failure
            result[i] = {
              status: "rejected".reason: error,
            };
            // The execution is complete
            if(count === totalCount) { resolve(result); }});if (count === 0) { resolve(result); }}}catch(error) { reject(error); }}); };Copy the code

Promise.race

Based on learning

The race approach is visually a race mechanism that recognizes only the first runner, whether successful or unsuccessful.

The promise.race () method again takes multiple Promise instances and wraps them into a new Promise instance.

const p = Promise.race([p1, p2, p3]);
Copy the code

In the above case, as long as one instance of P1, P2 and P3 changes the state first, the state of P will change accordingly. The return value of the first changed Promise instance is passed to p’s callback.

const p1 = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        resolve(1)},1000)})const p2 = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        reject(2)},2000)})const p3 = 3;

// Success first, failure later
Promise.race([p1, p2]).then(res= > {console.log(res)}) / / 1
// Synchronous first, asynchronous later
Promise.race([p1, p3]).then(res= > console.log(res)) / / 3
// String
Promise.race('zc').then(res= > console.log(res)) // z
Copy the code

Thought analysis

The Race method is less convoluted and returns a promise as soon as it changes state.

So we simply listen for each promise’s then and catch methods, and call resolve and Reject when state changes.

The source code to achieve

Promise.race(promises) {
    return new Promise((resolve, reject) = > {
        for (let p of promises) {
            // promise. resolve converts p to prevent passing in non-Promise instances
            // The race execution mechanism returns the result of a state change for that instance
            // So listen
            Promise.resolve(p).then(resolve).catch(reject); }})}Copy the code

Promise.any

Based on learning

The any method is figuratively chosen only if the first one succeeds. If all fail, the failure case is returned.

ES2021 introduces the promise.any () method. The method takes a set of Promise instances as parameters and returns them wrapped as a new Promise instance.

The any method is very similar to the race method in that it also has the short circuit characteristic. As long as one instance becomes a depressing state, the successful result will be returned. If all fail, a failure case is returned.

// The promise of success
const p1 = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        resolve(1)},1000)})// Failed promise
const p2 = new Promise((resolve, reject) = > {
    setTimeout(() = > {
        reject(2)},2000)})// Failed promise
const p3 = new Promise((resolve, reject) = > {
    reject(3)})// There is a promise of success
Promise.any([p1,p2]).then(res= > console.log(res))/ / 1

// All failed promises
Promise.any([p2,p3]).then(res= > console.log(res))
                    .catch(error= > console.log(error)) // AggregateError: All promises were rejected
                    
/ / type String
Promise.any('zc').then(res= > console.log(res)) // z
Copy the code

Through the above output results, we can find that:

  • anyThe method is also acceptableIteratorFormat parameters
  • When apromiseInstance transformation tofulfilledWhen,anyReturn successfulpromise, the value is the earliest successfulpromiseValue.
  • whenpromiseWhen all else fails,anyReturn failedpromise, and the value is fixed asAggregateError: All promises were rejected

Thought analysis

Above we analyzed the mechanism of the any method:

  1. An instance is converted tofulfilledanyThen return successfulpromise. So we can do something similar hereraceThe method of monitoring eachpromiseThe success of the.
  2. All instances are converted torejectedanyreturnAggregateError: All promises were rejected. Here we can refer toallMethod returns success only after all of its successes, so we need to accumulate the number of failures whenrejectCount === count, returns the failure value.

The source code to achieve

Promise.any = function(promises) {
    return new Promise((resolve,reject) = > {
        let count = 0;
        let rejectCount = 0;
        let errors = [];
        let i = 0;
        for (let p of promises) {
            i = count;
            count ++;
            Promise.resolve(p).then(res= > {
                resolve(res)
            }).catch(error= > {
                errors[i] = error;
                rejectCount ++;
                if (rejectCount === count) {
                    return reject(new AggregateError(errors))
                }
            })
        }
        if(count === 0) return reject(new AggregateError('All promises were rejected'))})}Copy the code

After the language

I am battlefield small bag, a fast growing small front end, I hope to progress together with you.

If you like xiaobao, you can pay attention to me in nuggets, and you can also pay attention to my small public number – Xiaobao learning front end.

All the way to the future!!

An early end to the epidemic will restore peace to the world