Focus onThe front small OuRead more original technical articles

Review: Promise source progressive interpretation (a) review: Promise source progressive interpretation (two) review: Promise source progressive interpretation (three) review: Promise source progressive interpretation (four)

Complete code + notes, can be read

The Promise constructor, finally, wraps isArray to determine whether an object is an array:

/* isArray: check whether the object is an array */
function isArray(x) {
  return Boolean(x && typeofx.length ! = ='undefined')}Copy the code

Promise. All – the source code

/** The all property of the Promise constructor points to the function * argument arr: array */
Promise.all = function (arr) {
  // Return a new contract
  return new Promise(function (resolve, reject) {
    if(! isArray(arr)) {return reject(new TypeError('Promise.all accepts an array')) // The arguments must be arrays
    }
    var args = Array.prototype.slice.call(arr) // Array prototype slice method, using call binding to ARR (avoid custom slice method)
    if (args.length === 0) return resolve([]) // If the array length is 0, the executor function is immediately executed and returns with an empty array
    New Promise((resolve, reject) => resolve([]))

    var remaining = args.length

    /** * res() method * parameter I: array subscript * parameter val: array item */
    function res(i, val) {
      try {
        // console.log(args[I], val) // Args [I] and val are originally the same

        /* If the item is an object or function object, special treatment is done to its then attribute */
        if (val && (typeof val === 'object' || typeof val === 'function')) {
          var then = val.then
          // If then refers to a function (val is a Promise or thenable object), then processing is done
          if (typeof then === 'function') {
            /* Then () Promise () {/ / This will be a pity and onRejected, Then () -> create Handler instances (this refers to the Promise instance before then, which is the item itself) -> call handle. The next operation will be performed according to _state -> if _state is 1, then call the promise._IMMEDIateFN -> Call onFulfilled (that is, function(val)) with the parameter value of about _value. Call function(self._value) (if _state is 0, the Handler instance is put into the _DEFERrends array of the Promise instance before then(), the synchronization is suspended, the whole code is terminated, and the item is returned) */
            then.call(
              val,
              function (val) {
                res(i, val) // Call the res method again with the _value of the term as val
              },
              reject
            )
            return}}/* Override item: if the item is a resolved term, it is overridden as its resolved value/reason for rejection, if it is non-term, it is unchanged */
        args[i] = val
        // console.log(args[i], val)
        // console.log(args)

        /* If all promises are completed (none pending), the executor function's resolve callback takes the processed array */
        if (--remaining === 0) {
          resolve(args) // Done inside doResolve() controls the resolve/reject method to execute only once}}catch (ex) {
        /* If one of the items fails, all of them are executed and catch is executed */
        reject(ex) // Done inside doResolve() controls the resolve/reject method to execute only once}}/* Loop through an array, using the res() method */ for each entry
    for (var i = 0; i < args.length; i++) {
      res(i, args[i])
    }
  })
}
Copy the code
  • parameterIt must beAn array ofIf, forAn empty arrayIs equivalent tonew Promise((resolve, reject) => resolve([]))
  • An array of cyclic parameters, overridden as its resolution value/rejection reason if the entry is a resolution term, unchanged if it is not a resolution term
  • If all thePromiseAll execute (no pending), then execute the executor functionresolveCallback to the processed array
    • If there is a pending contract, it will wait until the execution is complete
  • As long as one of them is abnormal, all execute exit, entercatchException handling

Promise.all – Phase testing

setTimeout(
  console.log,
  0.Promise.all(), // Arguments are not arrays
  /* Promise { _state: 2, _handled: false, _value: 'TypeError: Promise.all accepts an array', _deferreds: null } */
  Promise.all([]), // The argument is an empty array
  /* Promise { _state: 1, _handled: false, _value: [], _deferreds: null } */
  new Promise((resolve, reject) = > resolve([])), // Equivalent to promise.all ([])
  Promise.all([1.2.3]), // The arguments are arrays, each of which is not a Promise object
  /* Promise { _state: 1, _handled: false, _value: [ 1, 2, 3 ], _deferreds: null } */
  Promise.all([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)),// The arguments are arrays, each of which is a resolved Promise object
  /* Promise { _state: 1, _handled: false, _value: [ 1, 2, 3 ], _deferreds: null } */
  Promise.all([
    Promise.resolve(true),
    Promise.resolve(true),
    Promise.resolve(true),]),/* Promise { _state: 1, _handled: false, _value: [true, true, true], _deferreds: null } */
  Promise.all([Promise.resolve(1), Promise.reject(2), Promise.resolve(3)),// The argument is an array with rejected Promise objects
  /* Promise { _state: 2, _handled: false, _value: 2, _deferreds: null } */
  Promise.all([Promise.resolve(1), new Promise(() = > {}), Promise.resolve(3)]) // The argument is an array with pending Promise objects
  /* Promise { _state: 0, _handled: false, _value: undefined, _deferreds: null } */
)
Copy the code

Promise. Race – the source code

/** The race property of the Promise constructor points to the function * argument arr: array */
Promise.race = function (arr) {
  // Return a new contract
  return new Promise(function (resolve, reject) {
    if(! isArray(arr)) {return reject(new TypeError('Promise.race accepts an array')) // The arguments must be arrays
    }
    /* Loop through an array, execute the resolve() and.then() methods for each item (if the argument is an empty array, do not execute, return the pending date) */
    for (var i = 0, len = arr.length; i < len; i++) {
      /** * promise.resolve () method * arr[I] : array item */
      Promise.resolve(arr[i]) // Return to the new term
        /* this is a big pity and onFulfilled. /* This is a big pity and onFulfilled. Then () -> create Handler instance (this refers to the Promise instance before THEN, which promise.resolve () returns) -> call handle. The next operation will be performed according to the _state -> if the _state is 1, then call the promise._IMMEDIateFN -> Call ondepressing with the parameter value of about _value, that is, call function(self._value) */
        .then(resolve, reject) // The done inside doResolve() controls that the resolve/reject method is executed only once, so only the first Promise executes resolve/reject}})New Promise((resolve,reject)=>{promise.resolve (3).then(resolve, reject)})
}
Copy the code
  • The argument must be an array, or an empty array returns a pending date
  • Loop through an array of parameters, called in orderPromise.resolve, the parameter is an array entry
  • Only those settled firstPromiseAccording to its stateresolve/reject, the rest are not executed
  • Comparison of core ideas:
    • Promise.all:resolveArray of all items (if the item is a term, it is replaced with a settlement value/rejection reason)
    • Promise.race: one by onePromise.resolveArray item (no longer if the term returned has been resolved/rejectedPromise.resolveThe following term)

Promise.race – Stage test

setTimeout(
  console.log,
  0.Promise.race(), // Arguments are not arrays
  /* Promise { _state: 2, _handled: false, _value: 'TypeError: Promise.race accepts an array', _deferreds: null } */
  Promise.race([]), // The argument is an empty array
  /* Promise { _state: 0, _handled: false, _value: undefined, _deferreds: null } */
  Promise.race([3.2.1]), // The arguments are arrays, each of which is not a Promise object
  /* Promise { _state: 1, _handled: false, _value: 3, _deferreds: null } */
  Promise.resolve(3), // Equivalent to promise.race ([3, 2, 1])
  /* Promise { _state: 1, _handled: false, _value: 3, _deferreds: null } */
  Promise.race([Promise.resolve(3), Promise.resolve(2), Promise.resolve(1)),// The arguments are arrays, which are the first Promise objects to resolve
  /* Promise { _state: 1, _handled: false, _value: 3, _deferreds: null } */
  Promise.race([Promise.reject(1), Promise.resolve(2), Promise.resolve(3)),// The argument is an array that first sets the rejected Promise object
  /* Promise { _state: 2, _handled: false, _value: 1, _deferreds: null } Possible Unhandled Promise Rejection: 1 */
  Promise.race([new Promise(() = > {}), Promise.resolve(2), Promise.resolve(1)]) // The arguments are arrays, which are the first Promise objects to resolve
  /* Promise { _state: 1, _handled: false, _value: 2, _deferreds: null } */
)
Copy the code

Promise. Prototype. Finally, the source code

/** The finally property of the Promise prototype points to the function * parameter callback: onFinally handler that executes the final callback */ after the Promise is fulfilled or rejected
Promise.prototype['finally'] = function (callback) {
  // console.log(this, 'finally') // this points to the Promise instance returned before finally()
  // console.log(this.constructor) // constructor points to the Promise constructor
  // console.log(this.constructor === Promise) // true
  var constructor = this.constructor/ * callPromise.prototype.then() method, the following textsetTimeoutFor example, run the following command: -> CreateHandlerInstance (thisPoint to thefinally(In front of)PromiseInstance), create a newPromiseInstance -> callhandle(),_stateIs 1, callPromise._immediateFn, the callonFulfilled, returns after the callPromiseExample (see call procedureonFulfilledInternal) -> callresolve(), the incomingHandlerThe instancepromiseandonFulfilledThe return value (is onePromiseInstance), will_stateAssign it to 3,_valueThe assignment foronFulfilledThe returnedPromiseInstance -> callfinale(), the_deferredsFor [], assign tonullEnd -> Return.then() internally createdPromiseExample:Promise { 
          _state: 3._handled: false._value: Promise {
            _deferreds: null._handled: false._state: 1._value: 2,}_deferreds: null} * /return this.then(
    / / this is a big pity
    function (value) {
      /* Call the procedure, as in the following setTimeout test: Call promise.resolve () to create a resolved promise.resolve (). The resolve value is set to 3. Promise { _state: 1, _handled: false, _value: 3, _deferreds: Then () (inner this points to the Promise. Resolve () return date, _state is 1, _value is 3), create Handler instance (only ondepressing), Create a new Promise instance -> call Handle () with an internal state of 1, set its handled to true, call promise._IMMEDIateFN, and call onFulfilled (i.e. Function () {return value}), finally before the Promise instance _value, that is, 2 -> call resolve(), pass in the Handler instance Promise and ondepressing return value 2, Call finale(), whose _deferreds is [], and call null -> Return. Then () Creates an internally created Promise instance (as a parameter, The Promise instance passed to the upper resolve() method returns: Promise {_state: 1, _handled: false, _value: 2, _deferreds: null} */
      // return constructor.resolve(callback())
      return constructor.resolve(callback()).then(function () {
        // console.log(value) // finallyReturn before ()PromiseThe resolution value of the instancereturn value
      }/ *)}onRejectedHandler */function (reason) {
      return constructor.resolve(callback()).then(function () {
        // console.log(reason) // finallyReturn before ()PromiseThe reason for rejecting the instancereturn constructor.reject(reason) //Different from ondepressing, the internal returns the period of rejection, and the reason for rejection isfinally(before)PromiseInstance rejection reason /*PromiseThe final step of the._ImmediateFN is to call the resolve() method. If the second parameter is not a date, the _state value will be assigned in resolve()1So to distinguish the difference from ondepressing, return the period of rejection (rather than the reason for the rejection), the second parameter is the period, and the _state value is assigned in resolve()3Therefore, the term returned will be nested one more layerPromise(onFulfilled nesting2Layer, onRejected nested3Layer), the innermost layer is the term of this rejectionPromise { 
            _state: 3,
            _handled: false,
            _value: Promise {
              _deferreds: null,
              _handled: false,
              _state: 3,
              _value: Promise { //The innermost stage is the period of rejection _deferreds:null,
                _handled: false,
                _state: 2.//Where _state _value:2
              }
            }
            _deferreds: null* /}}} /* Inner layerPromiseInstance assigned to the outer layerPromiseThe instance_value, layer by layer assignment recursive */)}Copy the code

Promise. Prototype. Finally – phase test

setTimeout(
  console.log,
  0.Promise.resolve(2).finally(() = > {
    console.log('finally3') / / print 'finally3'
    return 3
  })
  Promise {_state: 1, _handled: false, _value: Promise {_state: 0, _handled: false, _value: undefined, _deferreds: null }, _deferreds: null } */
  Promise.reject(2).finally(() = > {
    console.log('finally4') / / print 'finally4'
    return 4
  })
  Promise {_state: 3, _handled: false, _value: Promise {_state: 3, _handled: false, _value: Promise { _state: 2, _handled: false, _value: 2, _deferreds: null }, _deferreds: null }, _deferreds: null } */
)
Copy the code

Promise. AllSettled – the source code

/** The allSettled property of the Promise constructor points to the function parameter ARr: array */
Promise.allSettled = function (arr) {
  // console.log(this) // this points to the Promise constructor
  var P = this

  // Return a new contract
  return new P(function (resolve, reject) {
    // The arguments must be arrays
    if(! (arr &&typeofarr.length ! = ='undefined')) {
      return reject(
        new TypeError(
          typeof arr +
            ' ' +
            arr +
            ' is not iterable(cannot read property Symbol(Symbol.iterator))'))}var args = Array.prototype.slice.call(arr) // Array prototype slice method, using call binding to ARR (avoid custom slice method)
    if (args.length === 0) return resolve([]) // If the array length is 0, the executor function is immediately executed and returns with an empty array
    New Promise((resolve, reject) => resolve([]))

    var remaining = args.length

    /** * res() method * parameter I: array subscript * parameter val: array item */
    function res(i, val) {
      // console.log(args[I], val) // Args [I] and val are originally the same

      /* If the item is an object or function object, special treatment is done to its then attribute */
      if (val && (typeof val === 'object' || typeof val === 'function')) {
        var then = val.then
        // If then refers to a function (val is a Promise or thenable object), then processing is done
        if (typeof then === 'function') {
          /* Then () Promise () {/ / This will be a pity and onRejected, Then () -> create Handler instance (this refers to the Promise instance before then, that is, the item itself) -> call handle. The next operation will be performed according to _state -> if _state is 1, then call the promise._IMMEDIateFN -> Call onFulfilled (that is, function(val)) with the parameter value of about _value. Call function(self._value) (if _state is 0, the Handler instance is put into the _DEFERrends array of the Promise instance before then(), the synchronization is suspended, the whole code is terminated, and the item is returned) */
          then.call(
            val,
            function (val) {
              res(i, val) // Call the res method again with the _value of the term as val
            },
            function (e) {
              args[i] = { status: 'rejected'.reason: e } // rewrite this to {status:'rejected',reason: error cause}
              /* If all items are completed, the executor function's resolve callback is executed with the processed array */
              if (--remaining === 0) {
                resolve(args)
              }
            }
          )
          return}}/* Rewrite this item: This will be a big pity, which is {status:'fulfilled',value: fulfilled value} this will be a big pity, which is {status:'rejected',reason: rejected reason} This will be rewritten as the object {status:' depressing ',value: item} */
      args[i] = { status: 'fulfilled'.value: val }
      // console.log(args[i], val)
      // console.log(args)

      /* If all promises are completed (none pending), the executor function's resolve callback takes the processed array */
      if (--remaining === 0) {
        resolve(args) // Done inside doResolve() controls the resolve/reject method to execute only once}}/* Loop through an array, using the res() method */ for each entry
    for (var i = 0; i < args.length; i++) {
      res(i, args[i])
    }
  })
}
Copy the code
  • parameterIt must beAn array ofIf, forAn empty arrayIs equivalent tonew Promise((resolve, reject) => resolve([]))
  • Loop parameter array, rewrite:
    • If this isThe date of settlement, is rewritten as{status:' depressing ',value: fulfilled value}
    • If this isRejection of the contract, is rewritten as{status:'rejected',reason: rejected reason}
    • If this isThe issue aboutIs overwritten as an object{the status: 'fulfilled, value: the}
  • If all thePromiseAll execute (no pending), then execute the executor functionresolveCallback to the processed array
    • If there is a pending contract, it will wait until the execution is complete

Promise.allSettled – Stage test

setTimeout(
  console.log,
  0.Promise.allSettled(), // Arguments are not arrays
  /* Promise { _state: 2, _handled: false, _value: 'TypeError: undefined undefined is not iterable(cannot read property Symbol(Symbol.iterator))', _deferreds: null } */
  Promise.allSettled([]), // The argument is an empty array
  /* Promise { _state: 1, _handled: false, _value: [], _deferreds: null } */
  Promise.allSettled([3.2.1]), // The arguments are arrays, each of which is not a Promise object
  /* Promise { _state: 1, _handled: false, _value: [ { status: 'fulfilled', value: 3 }, { status: 'fulfilled', value: 2 }, { status: 'fulfilled', value: 1 } ], _deferreds: null } */
  Promise.allSettled([
    Promise.resolve(3),
    Promise.resolve(2),
    Promise.resolve(1),]),// The arguments are arrays, each of which is a resolved Promise object
  /* Promise { _state: 1, _handled: false, _value: [ { status: 'fulfilled', value: 3 }, { status: 'fulfilled', value: 2 }, { status: 'fulfilled', value: 1 } ], _deferreds: null } */
  Promise.allSettled([
    Promise.resolve(1),
    Promise.reject(2),
    Promise.resolve(3),]),// The parameters are arrays, each of which is a Promise object with a reject date
  /* Promise { _state: 1, _handled: false, _value: [ { status: 'fulfilled', value: 1 }, { status: 'rejected', reason: 2 }, { status: 'fulfilled', value: 3 } ], _deferreds: null } */
  Promise.allSettled([
    Promise.resolve(2),
    new Promise(() = > {}),
    Promise.resolve(1),])// The parameters are arrays, each of which is a Promise object with a pending date
  /* Promise { _state: 0, _handled: false, _value: undefined, _deferreds: [] } */
)
Copy the code

Summary of Implementation results

  • implementationPromiseConstructor method:Promise,all,Promise.race,Promise.allSettled
  • implementationPromisePrototype method:Promise.prototype.finally

As of the code in this section →