In the process of writing node.js, continuous IO operations can lead to "pyramid Nightmare", the callback function of multiple nesting makes the code difficult to maintain, using COMMONJS promise to encapsulate asynchronous functions, using a unified chain API to escape the nightmare of multiple callbacks.
The non-blocking IO model provided by Node.js allows us to handle IO operations in the form of callback functions, however, when continuous IO operations are required, your callback functions are nested, the code is very difficult to maintain, and there may be many duplicate codes for error handling, known as "pyramid of Doom ".
Copy Code code as follows:
Step1 (function (value1) {
Step2 (value1, function (value2) {
Step3 (value2, function (value3) {
STEP4 (Value3, function (value4) {
Do something with Value4
});
});
});
});
This is actually the problem of node.js control flow, the solution for this problem is many, such as the use of async, or Eventproxy, but the theme of this article is to use the COMMONJS specification of promise to solve this problem.
What is promise?
There are many kinds of promise specifications for COMMONJS, and we generally discuss the promise/a+ specification, which defines the basic behavior of promise.
Promise is an object that typically represents an asynchronous operation that might be completed in the future. This operation may or may not succeed, so a promise object typically has 3 states: Pending,fulfilled,rejected. Represents incomplete, successful completion, and operation failure, respectively. Once the state of the Promise object changes from pending to fulfilled or rejected any one, its state cannot be changed again.
A promise object usually has a then method that allows us to manipulate the values that might be returned after a future success or the cause of the failure. The then method is this:
Promise.then (onfulfilled, onrejected)
Obviously, the then method accepts two parameters, which are usually two functions, one to handle the successful outcome of the operation, and the other to handle the reason for the failure of the operation, the first parameter of which is the result of success and the reason for failure. If a function is not passed to the then method, then this parameter is ignored.
The return value of the then method is a Promise object, which allows us to chain-call then to achieve the effect of the control process. There are a lot of details about the problem, such as the value of the transfer or error handling. The promise specification is defined in this way:
The return value of the onfulfilled or onrejected function is not a promise object, the value will be the first argument of onfulfilled in the next then method, if the return value is a Promise object, How to then the return value of the method is the Promise object
If an exception is thrown in the onfulfilled or onrejected function, the return Promise object state of the then method is converted to rejected if the Promise object invokes then, The Error object is used as the first parameter of the onrejected function
If the promise state changes to fulfilled and the onfulfilled function is not supplied in the then method, the then object state returned by the promise method changes to fulfilled and succeeds to the result of the previous promise. Rejected the same.
In addition, both onfulfilled and onrejected are executed asynchronously.
Implementation of the specification: Q
The above is the specification of promise, and what we need is its implementation, Q is a library that has a good implementation specification for promise/a+.
First we need to create a Promise object, the specification of the Promise object creation in promise/b, here without a detailed explanation, directly on the code.
Copy Code code 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 promise implementations are much the same in promise creation, by creating a defer object with a Promise property, calling Defer.resolve (value) If a value is successfully obtained, and calling Defer.reject if it fails ( Reason), finally returns the defer Promise property. This process can be understood as invoking defer.resolve to change the state of the promise into fulfilled, and calling Defer.reject to promise the state of the rejected.
How do you use promise to write beautiful code in the face of a series of sequential asynchronous methods? Look at the following example.
Copy Code code 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), so that if a series of asynchronous methods always return a value successfully, the code falls down, If either of these asynchronous methods fails or an exception occurs, the function in the catch is executed according to the COMMONJS promise specification. Q also provides a finally method, which is literally well understood, that is, whether resolve or reject, eventually the function in finally is executed.
It looks good, the code is more maintainable and aesthetically pleasing, so what if you want concurrency?
Copy Code code 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, calling the all method and passing a promise array to continue using the then chain style. There is also a good way to unify the code format, such as Q.nfbind, which can transform Node.js's native API into promise. More APIs are not detailed here.
Conclusion
This article mainly introduces to solve the Node.js control flow problem by using promise, but promise can also apply to the front-end, EMCASCRIPT6 has provided native API support. It is important to note that promise is not the only solution, Async is also a good choice and provides a more user-friendly concurrency control API, but I think promise is more advantageous in encapsulating functions that have asynchronous methods.
Well, this article is here first, I hope to be able to help you.