> new Promise((resolve, reject) = > setTimeout(resolve, 1000.'foo'))  
> .then(console.log)  
> // foo (1s after)
Copy the code

The easiest way to understand and use Promise is to do as in the code above, providing an asynchronous result to resolve and then passing a custom function to the then method as the result handler. But what exactly are resolve and reject? Behind the scenes, what exactly is the basic way it works? Let’s take a look at it from a specification perspective.

Reference: ES8 Promise

TL; DR

  • Promises work in a similar way to callback, using an internal abstract operation Job to implement asynchrony
  • The resolve/reject functions in the Promise constructor are created internally, and the arguments passed in when they are called are the result of parsing, inserted into the Job queue along with the user-passed handlers that Promise already stores. The parameter passed in can also be a promise, which is useful inside promise.all /race.
  • Promise.prototype.then According to the current state of the Promise, the result stored in the Promise is immediately fetched and inserted directly into the Job queue together with the handler in the parameter or associated with the Promise first as the result handler. Then implicitly calls the Promise builder function to build a new Promise and return it.
  • Promise.all creates a new Promise, initializes an empty result array and a counter to count resolve promises, and then iterates, creating a Promise for each iteration value. Set the promise’s THEN to add the result and the counter to the result array — resolve the final result when the counter drops to zero.
  • Promise.race also creates a new primary Promise, and then creates another Promise for each iteration value, based on the limit that a Promise can resolve only once. Resolve will return the result of the primary promise resolve.

new Promise(executor)

We’ll start with the Promise constructor, which is the value of the Promise property of the global object, which is why we can call it directly in the browser environment, just like the String and Array constructors.

The first step of the new Promise(Executor) constructor is just like any other constructor. It builds a new object based on the Promise prototype and initializes several internal slots [[PromiseState]], [[PromiseResult]], [[PromiseFullfillReactions]], [[PromiseRejectReactions]], [[PromiseIsHandled]]] to record information about their actions. [[PromiseResult]] is “pending”, empty list, empty list, false.

Next, ES generates a resolve function for resolve Promises and a Reject function for Reject Promises based on the Promise object. Then call executor, taking the resolve and Reject function as arguments, and reject promise directly if something goes wrong in the process. Finally return the promise.

So what’s resolve and what’s reject. We know that the Promise state, [[PromiseState]], has three values: Reject, reject, reject, reject, reject, reject, reject, reject, reject, reject, reject The resolve function can be used to fullfill a promise from a pending state to a fullfilled state, or to reject a promise.

So what do the Resolve and Reject functions do?

Let’s start with the Reject function, which initializes [[Promise]] and [[AlreadyResolved]] slots when it’s generated, associating it with a Promise. [[AlreadyResolved]] is passed as reason and will be resolved only when [[AlreadyResolved]] is false Return RejectPromise, pass promise, and Reason arguments to reject Promise, otherwise return undefined. RejectPromise (promise, “reason), in addition to the [[PromiseState]] from pending to rejected, will put the result of the promise of the [[PromiseResult]] a value reason, Retrieve the records stored in the promise’s [[PromiseRejectReactions]] and invoke them with TriggerPromiseReactions for subsequent processing. Pass in reject for reason. Similarly, the FullfillPromise(promise, value) operation used in resolve function changes the state of the promise into a pity, Draw the value of [[PromiseFullfillReactions]] and call TriggerPromiseReactions, which will be fulfilled with the value.

TriggerPromiseReactions(argument) will call EnqueueJob(“PromiseJobs”, PromiseReactionJob, <

>), More on that later.
,>

The resolve function, like the Reject function, associates it with a promise when it’s generated. At execution time, we pass in a parameter called resolution. If a promise has already been resolved, undefined is returned. After that, things get a little more complicated.

  1. If the user passes the promise itself to resolve function as argument resolution, a TypeError is created, thrown, and calledRejectPromiseThe reason argument is this TypeError.
  2. If Resolution is not of type Object, it is calledFulfillPromise(promise, resolution).
  3. The rest is where resolution is a Promise with a then in addition to itself.
    • If resolution is an object without then, thenRejectPromise.
    • If there is a THEN attribute but it cannot be called, alsoFulfillPromise.
    • If there is a THEN attribute and it can be called, thenEnqueueJob("PromiseJobs", PromiseResolveThenableJob, <<promise, resolution, thenAction>>).

Before explaining EnqueueJob, let’s look at what a Job is. In simple terms, it is like an internal implementation mechanism for the callback: “When no other ES is running, initialize and execute its own ES. “. We have a queue of FIFO jobs to execute, and the execution environment running execution Context and execution context stack. The first Job in the QUEUE is executed when the latter two are empty.

ES specifies that there must be at least two Job queues, ScriptJobs and PromiseJobs. When we call EnqueueJob(“PromiseJobs”…) The jobs to be completed and their parameters are inserted into the PromiseJobs queue. As you can see, there are two kinds of jobs under promises

  1. PromiseReactionJob(reaction, argument)

    Reaction has three internal slots[[Capability]],[[Type]][[Handler]], respectively represent[[Associated promise and associated resolve function and reject function]],[[category]],[[handler]]. If the user does not give handler(undefined), the argument is taken as the result depending on whether the category is accepted or Reject. If given to handler, it is used to further process the argument. The resolve and reject functions are processed and returned based on the result.
  2. PromiseResolveThenableJob(promiseToResolve, thenable, then)

    Create the Resolve and Reject functions associated with promiseToResolve. Then is called, thenable is this, and resolve function and reject function are called with arguments.

Promise.prototype.then(onfulfilled, onrejected)

The first step is to create a promiseCapability, which contains a new promise and the associated resolve and Reject functions. A promise is made by building a promise as you would normally use the Promise constructor, but the pass to the executor constructor is automatically created internally, It records the resolve/ Reject function into PromiseCapability. According to promiseCapability and onfulfilled/ onfulfilled, create two PromiseReaction respectively for Fulfill and reject, that is, the final actions to be performed in PromiseJobs. If the current promise(this) is pending, insert the reactions into the [[PromiseFulfillReactions]] and [[PromiseRejectReactions]] queues of the promise, respectively. This will be a big pity or a pity. If the promise is fulfilled or rejected, you can remove the value “result” from the promise [[PromiseResult]] as a pity. Insert into the Job queue, EnqueueJob(“PromiseJobs”, PromiseReactionJob, <

>), and return the new promise stored in prjomiseCapability. Prototype. Catch (onrejected) then(undefined, onrejected)
,>

Promise.resolve(x)

Create a promiseCapability as then, then call the resolve function directly and pass in the value x to resolve, and return the new promise.

Promise.all(iterable)

Promise.all also creates a promiseCapability like then, which contains a new Promise and its associated resolve and Reject functions, followed by an iterator loop: 1. If the iteration is complete and the counter is 0 then the resolve function of promiseCapability is called to resolve the result array 2. Resolve also builds a new Promise, and then internally creates a Promise. All resolve Element Function, The then passed to this new Promise is used to add the result to the result array and decrement the counter by one.

Promise.race(iterable)

Similarly, create a promiseCapability, iterate, build a new Promise with promise. resolve, then call the then method of that new Promise, The resolve/reject function in promiseCapability is passed in, and the promise will resolve once.

conclusion

See here, I don’t know if you have a deeper understanding of Promise. Further down the line, the new async/await in ES6 is actually using the idea of Generator and Promise.


The text/Kacxxia

There is no author introduction

This article has been authorized by the author, the copyright belongs to chuangyu front. Welcome to indicate the source of this article. Link to this article: knownsec-fed.com/2018-08-22-…

To see more sharing from the front line of KnownsecFED development, please search our wechat official account KnownsecFED. Welcome to leave a comment to discuss, we will reply as far as possible.

Thank you for reading.