Focus onThe front small OuRead more original technical articles

Review: Progressive interpretation of Promise source code (a) review: progressive interpretation of Promise source code (two) review: progressive interpretation of Promise source code (three)

Complete code + notes, can be read

Multiple THEN concatenations – legacy issues

/* Not yet implemented: not less than 2. Then () chain calls */
new Promise((resolve, reject) = > {
  resolve(3)
})
  .then((res) = > {
    Handle () print self as Promise {_state: 1, _handled: true, _value: 3, _deferReds: []} will continue to execute the handler */ asynchronously
    return res
  })
  .then((res) = > {
    /* When calling the 2nd THEN, PROM is the date instance returned from the current THEN. PROM is the date instance returned from the 1st THEN, which is a newly created, unresolved date instance that puts Handler instances generated from the current THEN into the _Deferreds array of dates returned from the current THEN. Then pause and return handle() printing self as Promise {_state: 0, _handled: false, _value: undefined, _deferreds: [Handler {...}]} */
    console.log(res) // Do not print res. The second then and subsequent handlers are not implemented yet
  })
Copy the code
  • multiplethenChain calls from the secondthenTo start,thenBefore the returnedPromiseExamples arependingVoid contract instance of state, and therefore bothwillHandlerInstance in thethenBefore the returnedPromiseThe instance_deferredsAn array of
  • This section will explain it in detailhandle()andfinale()Two ways,It analyzesPromiseThe instance_deferredsThe array is inHandlerPost-instance operations, should be read repeatedly

The handle () – the source code

  • Finally arrivedhandler()The source code! It’s actually just a little bit better than the test code in the last sectionMainly observe multiplethenThe series of(Take 2 as examples)
/** Handle () method: core * self: Promise instance returned before the previous then() * deferred: Handler instance created this time */
function handle(self, deferred) {
  // console.log(self, 'handle')
  // console.log(deferred)
  [Function (anonymous)], // This is a big pity. OnFulfilled: // This is a big pity. OnFulfilled: // This is a big pity. [Function (anonymous)], // the promise attribute points to a new promise instance _state: 0, _handled: false, _value: undefined, _deferreds: [] } } */

  /* If the settlement value of the returned contract instance is of type PROMISE, _state=3 */
  while (self._state === 3) {
    self = self._value // Assign the resolution value to the returned contract instance
    // console.log(self)
  }

  /* If _state=0, the immediate contract instance is pendding (the onResolve or onReject handler has not yet been executed) */
  /* When a chained call is made, the Promise instance returned before the second or subsequent THEN () is always the new Promise instance, with a _state value of 0 */
  if (self._state === 0) {
    self._deferreds.push(deferred) // Put the Handler instance into the _deferrends array of the Promise instance returned before the previous THEN (). Since the Promise of the previous Handler instance pointed to the previous Promise instance, the previous Handler instance is affected accordingly
    // console.log(self, 'push')
    /* Promise { _state: 0, _handled: false, _value: undefined, _deferreds: [ Handler { onFulfilled: [Function (anonymous)], onRejected: [Function (anonymous)], promise: [Promise] } ] } */
    return // Synchronous execution is paused at this point, waiting for asynchronous execution (execute onResolve in the previous Promise's then)
  }

  /* If not, mark the current promise._handled as true */
  self._handled = true
  // console.log(self)

  /** Callback is handled asynchronously through the event loop * note: the event is executed asynchronously, and the second then is executed before the method
  Promise._immediateFn(function () {
    // console.log(deferred, '_immediateFn') When there are at least two. Then () Handler instances, the _deferreds promise instances that are generated by the former. Then () refer to the promise instance (which includes ondepressing or onRejected callback). _deferReds no longer points to an empty array but to an array containing the last Handler instance.)

    var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected // According to the _state before then() Promise, obtain the onFulfilled or onRejected handler
    // console.log(cb)

    /* If there is no onFulfilled or onRejected callback, then carry the current _value and wait for the callback of the next Promise object */
    if (cb === null) {
      // console.log(deferred.promise, self._value); (self._state ===1 ? resolve : reject)(deferred.promise, self._value)

      /** * resolve() or reject: wait for the callback of the next Promise object. Handler instance promise, which points to the promise instance before the previous THEN () * argument self._value: The _value property of the promise instance returned before the previous THEN () */
      // resolve(deferred.promise, self._value)
      // reject(deferred.promise, self._value)

      return
    }

    /* Perform your own callback */ if there is ondepressing or onRejected callback function
    var ret
    try {
      /** * () : perform onFulfilled or onRejected () * self._value: then() returns the fulfilled Promise instance value/reject reason */
      ret = cb(self._value) // Perform a callback that returns the value assigned to ret
    } catch (e) {
      /** * reject() method: callback method that processes the next catch * Parameter deferred. Promise: The promise property of the Handler instance created, pointing to the new Promise instance * parameter e: error message */
      reject(deferred.promise, e)
      return
    }

    /** * resolve() method: callback method to handle the next THEN * Parameter deferred. Promise: promise of Handler instance, pointing to the promise instance before the previous THEN () * parameter ret: return value to perform the current THEN callback */
    // console.log(deferred.promise, ret)
    resolve(deferred.promise, ret)
  })
}
Copy the code
  • The secondthenBefore the returnedPromiseThe instance must bependingState,So number twothenGenerated in theHandlerInstance will go into the second onethenBefore the returnedPromiseThe instance_deferredsAn array of
  • The key to the, rememberHandlerConstructor?
    • Call oncethen, all produce 1HandlerExamples, 2thenWe’re going to have 2 in seriesHandlerThe instance
    • eachHandlerThe instancepromiseBoth point to the currentthenGenerated in thePromiseThe instanceprom(That’s the next onethenBefore the returnedPromiseInstance)
    • But because of number twothenChanged the second onethenBefore the returnedPromiseInstance (_deferredsThe array into theHandlerInstance), therefore1.HandlerThe instance changes as well
    • Open thehandle()Comments at the endconsole.log(deferred.promise, ret)Can be better observedHandlerInstance change
    • In summary, this is the same promise instance that the promise attribute of the first Handler instance points to, and the _Deferreds array goes into the second Handler instance
  • Second pointThat is, after a handler is called, it is called againresolve()Method, guarantee number twothenI can get the first onethenThe return value of
    • Remember? inresolve()In, it will give it_stateand_valueAssign and callfinale()Methods. So we come to the final source code —finale()methods

The finale () – the source code

/** Call () method * argument self: instance */
function finale(self) {
  // console.log(self, 'finale')

  /* If _state is 2 (Promise executes reject()) and no callback is provided (or catch is not implemented), a warning is given */
  if (self._state === 2 && self._deferreds.length === 0) {
    /** * Execute the Promise constructor's _immediateFn() method * parameter fn: the warning method to execute */
    Promise._immediateFn(function () {
      /* If it is not processed, a warning is given */
      if(! self._handled) {/** * Executes the._unhandledrejectionfn () method of the Promise constructor with the argument self._value: rejection reason */
        Promise._unhandledRejectionFn(self._value)
      }
    })
  }

  /* Loop through self._deferreds, each of which executes the Handle () method */
  for (var i = 0, len = self._deferreds.length; i < len; i++) {
    /** * Handle () method * parameter self._Deferreds [I] : the current handle instance */
    // console.log(self, self._deferreds[i])
    handle(self, self._deferreds[i])
  }

  self._deferreds = null // After all, reset the _DeferReds array to NULL
}
Copy the code
  • Finally arrived_deferredsTime for arrays to really come into play!finale()It’s going to loop through the array and execute each itemhandle()
  • withhandleTogether, twothenThe series process is:
    • 1.thenBefore returningPromiseInstance (callresolve(),finale()._deferredsArray empty ends here)
    • Call the first onethen, the callhandle().Promise._immediateFnPut in asynchronous thread 1
    • Call number twothenThe second,thenBefore the returnedPromiseThe instance_statePhi is zero, and the second oneHandleInstance goes into the secondthenBefore the returnedPromiseThe instance_deferredsArray returns (thus changing the firstHandle)
    • Enter asynchronous thread 1 and execute thread 1thenAfter the processing method is called againresolve(),finale()._deferredsArray is not empty so calledhandle().Promise._immediateFnPut in asynchronous thread 2
    • Enter asynchronous thread 2 and execute thread 2thenAfter the processing method is called againresolve(),finale()._deferredsArray is empty all end
  • If the above process is not clear, the following is a step-by-step explanation using test examples

Chain calls for multiple THEN – stage tests

new Promise((resolve, reject) = > {
  resolve(3)
})
  .then((res) = > {
    console.log(res)
    return 4
  })
  .then((res) = > {
    console.log(res)
    return 5
  })
Copy the code
  • According to the source code, the complete call process of the above code is:
    • new Promise((resolve, reject) => {resolve(3)})
      • performnew PromiseTo createPromiseInstance, return thisPromiseThe instance
      • performdoResolve().Synchronization executes the actuator function immediately(resolve, reject) => {resolve(3)}
      • performresolve(3)That will bePromiseThe instance_stateFu is 1,_valueAssigned to 3
      • performfinale().PromiseThe instance_deferredsfor[]And assigned tonullAfter execution End
      • The returnedPromiseExample:Promise { _state: 1, _handled: false, _value: 3, _deferreds: null }
    • .then((res) => {console.log(res); return 4})
      • performPromise.prototype.thenTo create a newPromiseInstance, passing an empty method as the executor function, returns this new onePromiseThe instance
      • performnew Handler, packaging currentonFulfilledThe handler(res) => {console.log(res); return 4}To return toHandlerThe instance
      • performhandle()Pass in the previous onethen()Before the returnedPromiseInstance andHandlerThe instance
        • On aPromiseThe instance_statePhi is 1, and I put it_handledAssigned totrue, the implementation ofPromise._immediateFn(), will the currentonFulfilledHandler putAsynchronous thread 1
      • returnPromiseExample:Promise { _state: 0, _handled: false, _value: undefined, _deferreds: [] }
    • .then((res) => {console.log(res); return 5})
      • performPromise.prototype.thenTo create a newPromiseInstance, passing an empty method as the executor function, returns this new onePromiseThe instance
      • performnew Handler, packaging currentonFulfilledThe handler(res) => {console.log(res); return 5}To return toHandlerThe instance
      • performhandle()Pass in the previous onethen()Before the returnedPromiseInstance andHandlerThe instance
        • On aPromiseThe instance_stateIs 0, will this timeHanderInto which instances are put_deferredsAn empty array,returnAfter because there is no follow-up.then().Synchronous thread pause
        • On aPromiseThe instance becomes:Promise { _state: 0, _handled: false, _value: undefined, _deferreds: [ Handler {} ] }.HandlerFor theHandlerThe instance
        • The key to the: due to theHandlerThe instancepromisePoint to the.then()Created in thePromiseInstance (prom), soOn aHandlerInstances are also affected by itspromisePoint to thePromiseInstance (i.e., the previous onePromiseInstance)_deferredsPoint to the same[ Handler {} ]
      • Back to asynchronous thread 1Execute the previous oneHandlerInstance wrappedonFulfilledHandler prints 3 and returns 4
      • performresolve()Pass in the previous oneHandlerThe instancepromise(Pointing to changedPromiseExample) andonFulfilledReturns the value (4), which will_stateFu is 1,_valueFu is 4
        • That has changed at this timePromiseThe instance is updated toPromise { _state: 1, _handled: false, _value: 4, _deferreds: [ Handler {} ] }
      • performfinale(), passed in the updatedPromise, the loop_deferredsAn array of
      • performhandle(), passed in the updatedPromiseExamples and this oneHandlerThe instance
        • The updatedPromiseThe instance_statePhi is 1, and I put it_handledAssigned totrue, the implementation ofPromise._immediateFn(), will the currentonFulfilledHandler putAsynchronous thread 2(nested in asynchronous thread 1)
      • Due to theThere are no synchronous threads left. Let’s go straight to asynchronous thread 2, execute thisHandlerInstance wrappedonFulfilledHandler, prints 4, returns 5
      • performresolve(), passed into thisHandlerThe instancepromise(Unchanged, originalPromiseExample) andonFulfilledReturn the value (5), will_stateFu is 1,_valueAssigned to 5
        • At this timePromiseThe instance is updated toPromise { _state: 1, _handled: false, _value: 5, _deferreds: [] }
      • performfinale(), passed in the updatedPromise, its_deferredsfor[]And assigned tonullAfter execution End
      • returnPromiseExample:Promise { _state: 0, _handled: false, _value: undefined, _deferreds: [] }
  • To summarize again:
    • new PromisetheExecutor functionissynchronousIs executed first
    • No matter how many.then, itsCreate a newPromiseInstance, createHandleInstance andhandle()The first half of the method (untilPromise._immediateFnBefore)Are allsynchronousExecute in turn
    • At the back of the.thenWill change the previous returnPromiseInstance to change the previously generatedHandleThe instance
    • After the synchronization is complete, perform the first synchronization.thenIn thehandle()Asynchronous methods inPromise._immediateFnTo enable asynchronous threads
      • At the end of the asynchronous thread, executeresolve()Method reexecutionfinale()methods
      • Incoming at this pointPromiseThe instance_deferredsInstead of an empty array, we put the next one in.thenThe processing method in
      • And then execute it againhandle()Method and itsPromise._immediateFn
        • New asynchronous threads are nested within asynchronous threads until the final execution is complete

Chain calls alternating then and catch – stage tests

Promise.resolve(1)
  .catch((err) = > {
    console.log(3) // The onRejected handler is not executed after resolve
    return 3
  })
  .then((res) = > {
    console.log(res) / / 1
  })

Promise.reject(1)
  .then((res) = > {
    console.log(2) // Do not print, reject do not execute onResolved
    return 2
  })
  .catch((err) = > {
    console.log(err) / / 1
  })
Copy the code
  • resolveIt will not be executed lateronRejectedHandler,rejectNot executed lateronResolvedThe handler

There is no callback – phase test for the intermediate THEN or catch

new Promise((resolve, reject) = > {
  resolve(3)
})
  .then() // No callback, waiting for the next Promise callback
  .then((res) = > {
    console.log(res)
  })

new Promise((resolve, reject) = > {
  reject(4)
})
  .catch() // No callback, waiting for the next Promise callback
  .catch((res) = > {
    console.log(res)
  })
Copy the code
  • Carry current_valueValue, waiting for the next onePromiseObject callback
    • handle()In the methodPromise._immediateFnIn thecb===null, according to thethenbeforePromiseObject type (resolve/reject), callresolve()orreject()methods

Summary of Implementation results

  • Have been implemented:
    • multiplethen(catch)
    • thenwithcatchAlternate chain calls
    • In the middle of thethenorcatchChain calls without callbacks

As of the code in this section →