Welcome to pay attention to my public account “Life Code”
Take advantage of the New Year, will say Promise
Imagine that you are a top artist and fans spend their days and nights worrying about your upcoming song.
To relieve the pressure, you promised to send it to them after publication. You give your fans a list. They can fill in their email addresses so that when a song becomes available, it is immediately available to all subscribers. Even if something big goes wrong, like a studio fire, and you can’t release the song, they’ll still get notified.
Everyone is happy: you, because people don’t squeeze you anymore, and the fans, because they won’t miss the song.
Here’s a real-world analogy we often encounter in programming:
A “generate code” that does something and takes time. For example, some code to load data over a network. This is a singer.
Once the production code is ready, the consumption code wants its results. Many functions may require this result. These are the fans.
Promise is a special JavaScript object that links “produce code” and “consume code” together. According to our analogy: this is a “subscription list.” Generate code takes any time to generate the promised results, and commit makes the results available to all subscribed code when they are ready.
The analogy is not quite accurate, because JavaScript promises to be more complex than simple subscription lists: they have additional features and restrictions. But it was good from the start.
The constructor syntax for the Promise object is:
let promise = new Promise(function(resolve, reject) {
// executor (the producing code, "singer")
});
Copy the code
The function passed to the new Promise is called executor. The executor runs automatically when a new commitment is created. It contains the generated code that ultimately produces the results. To use the metaphor above: the executor is the singer.
Its arguments resolve and reject are callback functions provided by JavaScript itself. Our code is only inside the executor.
When the executor gets the result, it doesn’t matter whether it’s too late or too soon, it should call one of the following callback functions:
-
Resolve (value) – If the job completes successfully, the result value is used.
-
Reject (Error) – Error is the error object if an error occurs.
In summary: The executor runs automatically and tries to perform a job. When it finishes trying, it calls resolve if it succeeds and Reject if it fails.
The new PROMISE constructor returns a Promise object with the following internal properties:
State — Initially “pending”, then changed to “completed” when resolve is called and “Rejected” when reject is called.
Result — initially undefined, then changed to value when resolve(value) is called and error when reject(error) is called.
So the executor eventually moves the promise to one of the following states:
We’ll see how “fans” subscribe to these changes later.
Here is a Promise constructor and a simple executor function whose “generated code” takes time (via setTimeout):
let promise = new Promise(function(resolve, reject) {
// the function is executed automatically when the promise is constructed
// after 1 second signal that the job is done with the result "done"
setTimeout((a)= > resolve("done"), 1000);
});
Copy the code
Running the above code, we can see two things:
The executor is invoked automatically and immediately (via new Promise).
The actuator accepts two arguments :resolve and reject. These functions are predefined by the JavaScript engine, so we don’t need to create them. We’ll call one of them when we’re ready.
After one second of “processing,” the execution program calls resolve(” Finish “) to produce the result. This changes the state of the promise object:
This is an example of a successful job done, a “promise delivered”.
Here is an example of an executor refusing to commit by mistake:
let promise = new Promise(function(resolve, reject) {
// after 1 second signal that the job is finished with an error
setTimeout((a)= > reject(new Error("Whoops!")), 1000);
});
Copy the code
Reject (…). The call to the Rejected state moves the Promise object to the rejected state:
In summary, the actor should perform a piece of work (which usually takes time) and then call resolve or Reject to change the state of the corresponding Promise object.
Commitments that are resolved or rejected are referred to as “resolved” rather than the original “pending” commitment.
The executor should call only one resolve or one reject. Any change in state is final.
All further resolve and reject calls are ignored:
let promise = new Promise(function(resolve, reject) {
resolve("done");
reject(new Error("...")); // ignored
setTimeout((a)= > resolve("...")); // ignored
});
Copy the code
The idea is that there may be only one result or one mistake in the work done by the performer.
Similarly, resolve/ Reject expects only one argument (or None) and ignores the others.
In case something goes wrong, the executor should call Reject. This can be done with any type of argument (like resolve). But Error objects (or objects that inherit from Error) are recommended. The reasons for doing so will soon become obvious.
In practice, the executing program usually performs some operation asynchronously and calls resolve/reject after some time, but it does not need to do so. We can also call resolve or reject immediately, like this:
let promise = new Promise(function(resolve, reject) {
// not taking our time to do the job
resolve(123); // immediately give the result: 123
});
Copy the code
For example, this can happen when we start to do a job, but then see that everything has been done and cached.
That’s good. We immediately had a commitment to a settlement.