Many languages, in order to handle asynchronous patterns more like the usual order, contain an interesting library of scenarios called Promises,deferreds, or futures. The promises of JavaScript can facilitate separation of concerns in place of tightly coupled interfaces. This article is about JavaScript promises based on the PROMISES/A standard. [Http://wiki.commonjs.org/wiki/Promises/A] Use cases for promise:
Execution rules
Multiple Remote Authentication
Timeout processing
Remote Data request
Animation
Decoupling the event logic from the application logic
Elimination of the terror triangle of the callback function
Controlling asynchronous operations in parallel
JavaScript promise is an object that promises to return values in the future. is a data object with well-defined behavior. There are three possible states of promise:
Pending (tbd)
Rejected (reject)
Resolved (completed)
A promise that has been rejected or fulfilled belongs to the one already settled. A promise can only be changed from a pending state to a state that has been resolved. After that, the state of the promise will not change. A commitment can persist long after its corresponding processing has been completed. In other words, we can get the results of processing multiple times. We get the result by calling Promise.then (), which is returned only until the processing end of the commitment corresponds. We have the flexibility to make a bunch of commitments in tandem. These "then" functions in series should return a new commitment or the earliest one.
With this style, we can write asynchronous code just like we write synchronous code. Mainly through the combination of commitment to achieve:
- Stack-up tasks: Multiple places are scattered in the code, corresponding to the same commitment.
- Parallel tasks: Multiple commitments return the same commitment.
- Serial tasks: One commitment, then another commitment.
- The above several combinations.
Why so much trouble? Can't you just use the basic callback function?
Problems with callback functions
The callback function is suitable for simple repetitive events, such as making a form valid based on a click, or saving the result of a rest call. The callback function also causes the code to form a chain, a callback function calls a rest function, and a new callback function is set for the rest function, the new callback function calls another rest function, and so on. This creates a 1 destruction pyramid. The horizontal growth of the code is greater than the vertical growth. The callback function looks simple until we need a result and is immediately ready to be used in the next line of calculations.
Figure 1: Destroying the Pyramids
- ' Use strict ';
- var i = 0;
- function log (data) {Console.log ('%d%s ', ++i, data);};
- function Validate () {
- Log ("Wait for it ...");
- //Sequence of four long-running async activities
- SetTimeout (function () {
- Log (' result first ');
- SetTimeout (function () {
- Log (' result second ');
- SetTimeout (function () {
- Log (' result third ');
- SetTimeout (function () {
- Log (' result fourth ')
- }, 1000);
- }, 1000);
- }, 1000);
- }, 1000);
- };
- Validate ();
In Figure 1, I use a timeout to simulate an asynchronous operation. The way to manage exceptions is painful, and it's easy to play with leaky downstream behavior. When we write callbacks, the code organization becomes chaotic. Figure 2 shows a simulated validation stream that can run on Nodejs REPL. In the next section, we will migrate from the Pyramid-of-doom mode to a continuous promise.
Figure
- ' Use strict ';
- var i = 0;
- function log (data) {Console.log ('%d%s ', ++i, data);};
- Asynchronous FN Executes a callback result FN
- function Async (ARG, callBack) {
- SetTimeout (function () {
- Log (' result ' + arg);
- CallBack ();
- }, 1000);
- };
- function Validate () {
- Log ("Wait for it ...");
- //Sequence of four long-running async activities
- Async (' first ', function () {
- Async (' second ',function () {
- Async (' third ', function () {
- Async (' fourth ', function () {});
- });
- });
- });
- };
- Validate ();
Results in Nodejs repl execution
- $ node Scripts/examp2b.js
- 1 Wait for it ...
- 2 result First
- 3 Result Second
- 4 result Third
- 5 result Fourth
- $
I have encountered a angularjs dynamic validation of the case, based on the value of the corresponding table, dynamically restricting the value of the table item. The range of valid values for restricted items is defined on the rest service.
I wrote a scheduler that, depending on the value of the request, went to the function stack to avoid a callback nesting. The scheduler pops the function out of the stack and executes it. The callback of the function calls the scheduler back at the end until the stack is emptied. Each callback records all validation errors that are returned from the remote validation call.
I think the thing I wrote was an anti-pattern. If I use angular's $http to invoke the provided promise, my thinking will be more approximate in linear form throughout the validation process, just like synchronous programming. The flattened promise chain is readable. Go on...
Using promises
Figure 3 shows the way I'm going to rewrite the validation into a promise chain. The Kew Promise Library is used. Q Library is also available. To use the library, first use NPM to import the Kew library into Nodejs, and then load the code into Nodejs REPL.
Figure
- ' Use strict ';
- var Q = require (' Kew ');
- var i = 0;
- function log (data) {Console.log ('%d%s ', ++i, data);};
- Asynchronous FN Returns a promise
- function Async (ARG) {
- var deferred = Q.defer ();
- SetTimeout (function () {
- Deferred.resolve (' result ' + arg); \
- }, 1000);
- return deferred.promise;
- };
- Flattened promise Chain
- function Validate () {
- Log ("Wait for it ...");
- Async (' first '). Then (function (resp) {
- Log (RESP);
- return Async (' second ');
- })
- . Then (function (resp) {
- Log (RESP);
- return Async (' third ')
- })
- . Then (function (resp) {
- Log (RESP);
- return Async (' fourth ');
- })
- . Then (function (resp) {
- Log (RESP);
- }). Fail (log);
- };
- Validate ();
The output is the same as using nested callbacks:
- $ node Scripts/examp2-pflat.js
- 1 Wait for it ...
- 2 result First
- 3 Result Second
- 4 result Third
- 5 result Fourth
- $
The code is a little "tall", but I think it's easier to understand and modify. Easier to add appropriate error handling. Calling fail at the end of the chain is used to catch errors in the chain, but I can also provide a reject handler function in any of the then to do the corresponding processing.
Server or browser
Promises is as effective in the browser as it is in the Nodejs server. The following address, http://jsfiddle.net/mauget/DnQDx/, points to a jsfiddle that shows how to use a Promise Web page. Jsfiddle all the code is modifiable. A change in the browser output is shown in 4. I deliberately manipulate random movements. You can try to get the opposite result several times. It is possible to extend directly to multiple promise chains, just like the previous Nodejs example.
Figure 4: Single Promise
Parallel Promises
Consider an asynchronous operation to feed another asynchronous operation. Let the latter include three parallel asynchronous behaviors, which, in turn, feed the last action. Only if all parallel sub-requests pass through. As shown in 5. It was inspired by a dozen MongoDB operations that came into the encounter. Some are qualified for parallel operations. I realized the flow diagram of promises.
Figure 5: Structure of an asynchronous operation
How do we simulate parallel promises in the center row of the graph? The key is that the largest promise library has a full function that produces a parent promie that contains a set of sub-promises. When all the child promises passed, the parent promise passed. If a child promise is rejected, the parent promise refuses.
Figure 6 shows a snippet of 10 parallel promises each containing a literal promise. The last then method can be completed only if 10 subclasses pass through or if any subclasses are rejected.
- var promisevals = [' to ', ' Be, ', ' or ',
- ' Not ', ' to ', ' Being, ', ' that ',
- ' is ', ' the ', ' question ';
- var startparallelactions = function () {
- var promises = [];
- //Make a asynchronous action from each literal
- Promisevals.foreach (function (value) {
- Promises.push (makeapromise (value));
- });
- //Consolidate all promises into a promise of promises
- return Q.all (promises);
- };
- Startparallelactions (). Then (...
The following address, http://jsfiddle.net/mauget/XKCy2/, runs 10 parallel promises for Jsfiddle in a browser, randomly rejecting or passing. Here is the complete code for checking and changing the IF condition. Run again until you get an opposite finish. The positive results are shown in Figure 7.
Figure 7:jsfiddle Parallel promises example
Inoculation Promise
Many of the promise returned by the API have a then function--they are thenable. Usually I just deal with the result of the Thenable function by then. However, $q, Mpromise, and Kew libraries have the same API for creating, rejecting, or passing promise. Here are the API documentation linked to the reference section of each library. I don't usually need to construct a promise, except for the unknown description and the timeout function of the wrapper promise in this article. Please refer to which promises I created.
Promise Library Interoperability
Most JavaScript promise libraries interoperate at the then level. You can create a promise from an external promise, because promise can wrap any type of value. Then you can support cross-library work. In addition to then, the other promise functions may be different. If you need a function that your library does not contain, you can wrap a promise based on your library into a new one, based on the promise of the library that contains the function you need. For example, the promise of jquery is sometimes criticized. Then you can wrap it into Q, $q, mpromise, or promise in the Kew library.
Conclusion
Now I have written this article, but a year ago I was hesitant to hug the promise. I simply want to finish a job. I don't want to learn new APIs or break my original code (because I misunderstood promise). I used to think so wrongly! When I made a little note, it was easy to win the gratifying results.
In this article, I have simply given an example of a single promise,promise chain, and a parallel promise of promise. Promises is not difficult to use. If I can use them, anyone can. To view the complete concept, I support you by clicking on the Expert's written reference guide. Start with the promises/a reference, starting with the de facto standard JavaScript promise.
If you haven't used the promise directly, try it. Make up your mind: you'll have a great experience. I promise!
–lou Mauget, [email protected]
Reference links
Http://wiki.commonjs.org/wiki/Promises/A
https://github.com/bellbind/using-promise-q/
Https://github.com/Medium/kew
https://docs.angularjs.org/api/ng/service/$q
Https://github.com/aheckmann/mpromise
Http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use
https://gist.github.com/domenic/3889970
Http://sitr.us/2012/07/31/promise-pipelines-in-javascript.html
http://dailyjs.com/2014/02/20/promises-in-detail/
https://www.promisejs.org/
http://solutionoptimist.com/2013/12/27/javascript-promise-chains-2/
Http://www.erights.org/elib/distrib/pipeline.html
http://zeroturnaround.com/rebellabs/monadic-futures-in-java8/
Go: JavaScript promises is pretty cool: An interesting scenario library