This article examines the final catch, all, and race methods. The first is the catch method, and if you recall how the catch method is used, we usually put it at the end of the Promise chain to catch the reason for the rejection. Therefore, the catch method should also be defined on the prototype chain of the Promise. Let’s look at its implementation:

Promise.prototype['catch'] = function(onRejected) {
  return this.then(null, onRejected);
};
Copy the code

As you can see, the catch method is a low-profile version of the THEN method that accepts only a reject callback argument. Therefore, we conclude that the catch method is not the end of the Promise chain and can continue the chain call afterwards.

Looking at the RACE method, the RACE method takes an array and returns a Promise in the resolved or rejected state when any Promise in the array is resolved or rejected. Its implementation:

Promise.race = function(values) {
  return new Promise(function(resolve, reject) {
    for (var i = 0, len = values.length; i < len; i++) { values[i].then(resolve, reject); }}); };Copy the code

The entire method returns the instantiated Promise object, iterates through the array passed in as a callback argument, calls the then methods of each array element, and passes resolve, reject. Overall, the implementation is pretty neat, leaving the state of a Promise to the then methods of each array element, because once the state is decided it doesn’t change, so there’s no need for a tag to exit the loop halfway through.

Finally, the All method also takes an array and returns a fulfilled Promise object when all of the promises in the array are fulfilled, and a rejected Promise object when any Promise in the array is rejected. Its implementation:

Promise.all = function(arr) {
  return new Promise(function(resolve, reject) {
    if(! arr ||typeof arr.length === 'undefined')
      throw new TypeError('Promise.all accepts an array');
    var args = Array.prototype.slice.call(arr);
    if (args.length === 0) return resolve([]);
    var remaining = args.length;

    function res(i, val) {
      try {
        if (val && (typeof val === 'object' || typeof val === 'function')) {
          var then = val.then;
          if (typeof then === 'function') {
            then.call(
              val,
              function(val) {
                res(i, val);
              },
              reject
            );
            return;
          }
        }
        args[i] = val;
        if (--remaining === 0) { resolve(args); }}catch(ex) { reject(ex); }}for (var i = 0; i < args.length; i++) { res(i, args[i]); }}); };Copy the code

The whole method also returns an instantiated Promise object. Let’s look at the callback argument:

if(! arr ||typeof arr.length === 'undefined')
      throw new TypeError('Promise.all accepts an array');
Copy the code

An exception will be thrown if no parameter is passed in or if the parameter is passed without the length attribute. That is, you can pass in an array-like object instead of an array! The following:

 var args = Array.prototype.slice.call(arr);
Copy the code

Convert the parameters passed in to a real array, stored in the ARGS variable.

if (args.length === 0) return resolve([]);
Copy the code

If the length of the array passed in is 0, the Promise object is returned as an empty array.

var remaining = args.length;
Copy the code

Store the length of the array in the Remaining variable. Next we define an internal function res. Let’s see how it is called:

for (var i = 0; i < args.length; i++) {
      res(i, args[i]);
}
Copy the code

We iterate over the array, taking the array subscript and array elements as arguments, and call the res function. Let’s see what the res function does:

try {
  if (val && (typeof val === 'object' || typeof val === 'function')) {... } args[i] = val;if (--remaining === 0) { resolve(args); }}catch (ex) {
    reject(ex);
}
Copy the code

Ignore the first condition and assign val to the array element corresponding to its index, overwriting the original value. Aren’t the two values the same? We can guess that the last one must have done something to val. The value of the remaining variable decreases by 1 each time. If the value of the remaining variable is equal to 0, then resolve(args) is called. The Promise object that returns the completed state is an array of the elements passed in after processing. The order of its array elements has not changed! If an exception is thrown, reject is called, taking the cause as an argument, as we already know, and any rejected Promise object returns a single rejection reason, not an array! Now, the first conditional judgment, we can think about, what is the value that needs to be processed? If we call the all method, is the most likely value passed in a Promise object? Of course there are thenable objects, so only these values are processed!

if (val && (typeof val === 'object' || typeof val === 'function')) {
  var then = val.then;
  if (typeof then === 'function') {
    then.call(
      val,
      function(val) {
        res(i, val);
      },
      reject
    );
  return; }}Copy the code

The judgment that val is an object or function type, and that its then method is a function type, are validated as the Thenable type we talked about in part 1. If it is of type thenable, the then method of val is called, with val as this, and the callback is completed by recursively calling res, knowing that val is not of type thenable, and terminating the call.

Resolve (args) : resolve(args) : resolve(args) : resolve(args)

for (var i = 0; i < args.length; i++) {
   res(i, args[i]);
}
resolve(args);
Copy the code

Shouldn’t it be called after the array has been traversed? Notice that the RES function calls the THEN method, which executes asynchronously! So make sure all Promise states have changed before you call resolve(args)!

At this point, the source code for Promise-Polyfill is analyzed.

Promise-polyfill (1) Promise-polyfill (2)