Home >Web Front-end >JS Tutorial >Using Promise to encapsulate asynchronous functions in NodeJS_node.js

Using Promise to encapsulate asynchronous functions in NodeJS_node.js

WBOY
WBOYOriginal
2016-05-16 16:13:001124browse

In the process of writing Node.js, continuous IO operations may lead to a "pyramid nightmare". Multiple nesting of callback functions makes the code difficult to maintain. Use CommonJs's Promise to encapsulate asynchronous functions and use a unified chain. API to get rid of the nightmare of multiple callbacks.

The non-blocking IO model provided by Node.js allows us to use callback functions to handle IO operations. However, when continuous IO operations are required, your callback functions will be nested multiple times, making the code very unsightly and difficult to maintain. And there may be a lot of duplicate code for error handling, the so-called "Pyramid of Doom".

Copy code The code is as follows:

step1(function (value1) {
Step2(value1, function(value2) {
         step3(value2, function(value3) {
              step4(value3, function(value4) {
// Do something with value4
            });
        });
});
});

This is actually a problem with the Control flow of Node.js. There are many solutions to this problem, such as using async, or eventProxy, etc. However, the theme of this article is to use Promise in the CommonJs specification to solve this problem.

What is Promise?

There are many Promise specifications of CommonJs. We generally discuss the Promise/A specification, which defines the basic behavior of Promise.

A Promise is an object that usually represents an asynchronous operation that may be completed in the future. This operation may succeed or fail, so a Promise object generally has three states: Pending, Fulfilled, and Rejected. Represents incomplete, successful completion and operation failure respectively. Once the state of the Promise object changes from Pending to Fulfilled or Rejected, its state cannot be changed.

A Promise object usually has a then method, which allows us to manipulate the value returned after possible success in the future or the reason for failure. The then method looks like this:

promise.then(onFulfilled, onRejected)
Obviously, the then method accepts two parameters, which are usually two functions. One is used to handle the results after the operation succeeds, and the other is used to handle the reasons after the operation fails. The first of these two functions The two parameters are the result after success and the reason for failure respectively. If the parameter passed to the then method is not a function, this parameter will be ignored.

The return value of the then method is a Promise object. This feature allows us to chain call then to achieve the effect of controlling the flow. There are many detailed issues here, such as value transfer or error handling. The specification of Promise is defined as follows:

The return value of the onFulfilled or onRejected function is not a Promise object, then this value will be used as the first parameter of onFulfilled in the next then method. If the return value is a Promise object, why is the return value of the then method the Promise object?
If an exception is thrown in the onFulfilled or onRejected function, the status of the Promise object returned by the then method will be changed to Rejected. If the Promise object calls then, the Error object will be used as the first parameter of the onRejected function
If the Promise status becomes Fulfilled and no onFulfilled function is provided in the then method, the Promise object status returned by the then method becomes Fulfilled and the successful result is the result of the previous Promise. The same is true for Rejected.
In addition, onFulfilled and onRejected are executed asynchronously.

Standard implementation: q

The above is the specification of Promise, and what we need is its implementation. q is a library with better implementation specifications for Promise/A.

First we need to create a Promise object. The specifications for the creation of Promise objects are in Promise/B. I will not give a detailed explanation here, just go to the code.

Copy code The code is as follows:

Function(flag){
         var defer = q.defer();
             fs.readFile("a.txt", function(err, data){
If(err) defer.reject(err);
                 else defer.resolve(data);
            });
                return defer.promise;
}

Most implementations of Promise are similar in the creation of Promise. By creating a defer object with a promise attribute, if the value is successfully obtained, defer.resolve(value) is called. If it fails, defer.reject(reason) is called. Finally, return the promise attribute of defer. This process can be understood as calling defer.resolve to change the Promise's status to Fulfilled, and calling defer.reject to change the Promise's status to Rejected.

When faced with a series of continuous asynchronous methods, how to use Promise to write beautiful code? Take a look at the example below.

Copy code The code is as follows:

promise0.then(function(result){
              // dosomething
        return result;
}).then(function(result) {
              // dosomething
          return promise1;                                          }).then(function(result) {
              // dosomething
}).catch(function(ex) {
console.log(ex);
}).finally(function(){
console.log("final");
});

In the above code, the then method only accepts OnFulfilled, and the catch method is actually then(null, OnRejected). In this case, as long as a series of asynchronous methods always return values ​​successfully, the code will be waterfall-style. Running downward, if any of the asynchronous methods fails or an exception occurs, then according to the CommonJs Promise specification, the function in the catch will be executed. q also provides the finally method, which is easy to understand literally, that is, regardless of resolve or reject, the function in finally will be executed.

It seems good, the code is more maintainable and beautiful, so what if you want concurrency?

Copy code The code is as follows:
​ q.all([promise0, promise1, promise2]).spread(function(val0, val1, val2){
console.log(arguments);
                    }).then(function(){
console.log("done");
                  }).catch(function(err){
console.log(err);
                });

Q also provides an API for concurrency. Call the all method and pass a Promise array to continue using the chain style of then. There are also things like q.nfbind that can convert the native API of Node.js into Promise to unify the code format, which is also very good. More APIs will not be described in detail here.

Conclusion

This article mainly introduces the use of Promise to solve Node.js control flow problems, but Promise can also be applied to the front end. EMCAScript6 has provided native API support. It should be pointed out that Promise is not the only solution. async is also a good choice and provides a more friendly concurrency control API. However, I think Promise has more advantages when encapsulating functions with asynchronous methods.

Okay, this article ends here, I hope it can be helpful to everyone.

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn