Preface
A Promise object can be understood as an operation to be performed (often used for asynchronous operations). After using the Promise object, the code can be organized in a chain call way to make the code more intuitive. Moreover, because the existence of such a method as Promise.all, it can be made simple to perform multiple operations at the same time.
The rise of Promise is because in asynchronous method calls, callback functions often occur one after another. This situation has led to the emergence of callback pyramid problems. Not only is the code difficult and not beautiful to write, but it also makes it difficult for people who read the code to understand when the problem is complicated.
As an example:
db.save(data, function(data){ // do something... db.save(data1, function(data){ // do something... db.save(data2, function(data){ // do something... done(data3); // Return data}) });}); Suppose there is a database save operation, and a request requires the data to be saved three times in three tables. Then our code is similar to the above code. What should I do if there is a problem in the second db.save at this time? Based on this consideration, we need to use logic like try...catch in each layer of callback. This is the source of all evil, and it is also a point that node was widely criticized at the beginning.
Another disadvantage is that assuming there is no front-and-back dependency between our three saves, we still need to wait for the previous function to be executed before the next step is executed, and the three saves cannot be parallelized, and then return the result required after the three saves. (Or it requires skills to implement it)
Unfortunately, when I first started to get involved in node, I wrote a lot of hells like this.
Later, because I still wrote more front-end code, I came into contact with ES6 and found a powerful tool for solving the callback abyss.
In fact, long before the ES6 Promise, Q, when.js, bluebird and other libraries had already built their own promise wheels based on the Promise standard (refer to Promise/A+).
(I read an article and I think it makes sense. It says that you should not extend built-in native objects. This approach cannot be future-oriented. So here is a tip: be cautious when using libraries that extend native Promise.)
Only native Promise is discussed here.
ES6 Promise
Promise object status
Before explaining Promise in detail, let’s first take a theory:
Promise/A+ specification stipulates that the Promise object is a finite state machine.
It has three states:
1. pending (execution)
2. fulfilled (successful)
3. reject
where pending is the initial state, fulfilled and rejected are the end states (the end state indicates that the life cycle of the promise has ended).
The state transition relationship is:
pending->fulfilled, pending->rejected.
Various events (such as successful execution events, failed execution events, etc.) will be triggered as the state transitions.
Promise Form
Promise looks like this:
var promise = new Promise(function func(resolve, reject){ // do something, maybe async if (success){ return resolve(data); } else { return reject(data); }});promise.then(function(data){ // do something... eg console.log(data);}, function(err){ // deal the err.})The variable promise here is an instance of the Promise object.
When the promise object is created, the logic in func function will be executed.
When the logic is processed and there are no errors, the resolve callback will pass the value to a special place. Where is this special place? That is then in the following code. We use the callback function in then to process the result after resolve . For example, in the above code, we simply output the value to the console. If there is an error, reject it into the second callback function of then to process the error.
In line with the above theory of finite state machine, we know that when executing callback function code in the Promise constructor, the state is pending , the state is fulfilled after resolve , and the state is reject after reject
Promise data flow
The above is the first data flow of promise.
What is funny is that the then method of promise can still return a Promise object, so that we can use the next then to do the same processing.
The two callback functions in the first then determine what kind of Promise object the first then returns.
Assuming that the first callback of the first then does not return a Promise object, then the caller of the second then is still the original Promise object, except that its resolve value becomes the return value of the first callback function in the first then.
Assuming that the first then the first callback function returns a Promise object, the second then the caller becomes this new Promise object, and the second then waits for the new Promise object to resolve or reject and executes the callback.
Although I have spared a little, I feel that I am still very clear about it. Haha ~
If an error is encountered anywhere, the error is then handed over to the second callback function then with the second callback function to handle it. It can be understood that the error reject backward until it is processed.
In addition, the Promise object also has a method catch , which accepts a callback function to handle errors.
Right now:
promise.catch(function(err){ // deal the err.})Assuming that the handling of errors is similar, this method can centrally and uniformly handle errors. So other then methods do not require a second callback ~
Control concurrent promises
Promise has a "static method" - Promise.all (note that it is not promise.prototype). This method accepts an element that is an array of Promise objects.
This method also returns a Promise object. If all Promise objects in the array are resolve , then the resolve values will be used as an array as the resolve value of the (Promise object) of the return value of the Promise.all method, and can then be processed by the then method. If any Promise in the array is reject , then the reject value is the reject value of the return value of Promise.all method.
A very opaque point is:
The order of resolve value (as mentioned above, an array) received by the first callback function of the then method is the same as the order of the parameter array in Promise.all , rather than in chronological order.
There is also a method similar to Promise.all Promise.race , which also receives an array, except that it only accepts the first value resolve .
Turn other objects into Promise objects
The Promise.resovle method can return a Promise object as a parameter.
There are two situations:
Assuming that there is no .then method of the passed parameter, then the returned Promise object becomes resolve state, and the resolve value is the object itself.
Assuming that the passed parameter has a then method (called the thenable object), then the type of this object is changed to Promise, and its then method becomes Promise.prototype.then method.
Is Promise a solution to asynchronous?
Finally, let me say something very important: Promise's function is to solve the problem of callback pyramids, and it does not actually play a big role in controlling the asynchronous process. To truly use Promise to control the asynchronous process, we also need to use the ES6 generator function. (For example, the implementation of the co library of Tj master).
However, ES7 will have a more awesome solution: async/await , which is similar to co, but has native support. Let's wait and see.
Summarize
The above is all about the native Promise in Javascript ES6. I hope the content of this article will be helpful to everyone in learning ES6. If you have any questions, please leave a message to communicate.