JavaScript asynchronous programming solution

Source: Internet
Author: User
Tags call back generator http request readfile require setinterval what generator

I recently read some articles about javascript asynchronous programming and read the thin content several times <Async JavaScript>. To sum up, we will share it with you for future use.

First, there are several problems:

What are asynchronous programming/asynchronous functions?
What is the relationship between asynchronous functions and callback functions?
Why does asynchronous programming often appear at the same time as javascript?
What is the mechanism of asynchronous functions in javascript?
So what are the solutions for asynchronous programming?
What will javascript asynchronous programming look like in the future?

What is an asynchronous function?

For a jser, asynchronous programming occurs frequently during the process of learning and using javascrtip, perhaps second only to event-driven/single-thread. So what is asynchronous programming? What is an asynchronous function?

To put it bluntly, an asynchronous function is a function that will run a function from the event queue in the future. The focus of this is from the event queue. For this concept, we will analyze it later, now we only need to know that the asynchronous function will cause another function to run at a certain time in the future.

Asynchronous functions VS callback functions

It is also a high-frequency vocabulary, callback function. Again, I think it is necessary to differentiate between callback functions and asynchronous functions. Although many people do not have to worry too much about this distinction, we can use Lao Luo's words, I am not trying to win or lose. I am serious. The precise understanding and grasp of the concept is often the first step for us to study in depth.

The so-called return function:

In computer programming, a callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time. the invocation may be immediate as in a synchronous callback or it might happen at later time, as in an asynchronous callback. in all cases, the intention is to specify a function or subroutine as an entity that is, depending on the language, more or less similar to a variable. (from wikipedia)

From the wikipedia statement, we can clearly see that:

First, the callback function is passed into another piece of code as a parameter, that is, it emphasizes that the callback function needs to be passed into other code as a parameter;

Second, the callback function can be synchronous or asynchronous, depending on the user.

If we enter the wikipedia page, we can find some additional knowledge. For example, callback functions will appear in languages with certain features, then the function is a simple javascript, and of course the callback function is perfectly supported.

Now the two concepts should be clear. Let's take an example to compare them. For example:

Function callbackFunc (){
Console. log ("callback executed! ");
}

SetTimeout (callbackFunc, 10000 );

Function syncFunc (callbackFunc ){
CallbackFunc ();
}

In the code snippet above, setTimeou is an asynchronous function because it causes callbackFunc to run in about 1 second. CallbackFunc is a callback function for setTimeout. Meanwhile, callbackFunc is also a callback function for syncFunc, but is executed synchronously (executed in the same event loop), so syncFunc cannot be called an asynchronous function.

In addition, we can see in some articles on the Internet that many people have summarized the callback function as a solution for asynchronous programming, including four methods of javascript asynchronous programming by instructor Ruan Yifeng. I think this classification is inappropriate. If we think of callback functions as a solution for asynchronous programming, the distributed events, Promise, and powerful workflow control libraries we will talk about later are implemented in the form of callback functions, can it be regarded as the same solution? Therefore, I believe that callback functions cannot be simply used as a solution for asynchronous programming.

Asynchronous mechanism in javascript

Every jser should understand that javascript is single-threaded. Single-threaded means that only one task can be executed at a time, or only one function code segment can be executed. Then we are very prone to questions. If it is a single thread, how is asynchronous implemented?

One sentence: event-driven ).

First, javascript is executed in a single thread, but the platform of the javscript engine (browser or nodejs) has several threads. For example, for a browser, there is a thread for rendering, a thread for recording events (click), and a thread for Executing javascript, these threads are executed under the coordination control of the browser kernel (ui rendering is not allowed during javascript thread execution ). This is the asynchronous basis implemented by a single thread.

Secondly, each asynchronous function corresponds to at least one event-handler, and the event queue mentioned above is the place where event-handler should be placed when it is processed. The javascript engine thread processes a series of event-handler at the right time, and needs to meet two conditions at the right time:

This event has met the trigger condition (for example, about 1000 ms after setTimeout (func, 1000 );
Javascript threads are idle (for example, the callback delay condition registered with setTimeout has been met, but the javascript engine is doing a complex for loop that takes 3 seconds, the setTimeout callback function can only be executed after the for loop is executed ).
This is to mention the existence of event-loop. Every cycle is a tick, and its function is to continuously check whether event-handler exists in the event queue. If yes, event-loop will be retrieved and executed. We can understand event-loop as follows:

While (true ){
If (atLeasetOneEventIsQueued ){
FireNextQueuedEvent ();
    }
}
Finally, how can we determine if an event meets the trigger condition (the timing condition is the appropriate one in the above article?

The trigger conditions of different events may be monitored by different threads. For example, if we send an ajax request, the browser should have an independent thread that sends an http request and notifies the javascript engine thread to meet the trigger conditions when the request returns. click a button, the browser's GUI thread should notify the javascript engine, and then execute the corresponding event-handler in a timely manner.

For example, assume that we are on a page where setTimeout is executing a code segment with a latency of MS, we clicked a button because the event trigger conditions have been met and the javscript thread is idle. Therefore, according to our script, the browser will immediately execute another code bound to this event; the code triggered by clicking an event will do two things,

One is to register a setinterval and require a code segment to be executed every 700ms.
The other is to send an ajax request and request that a code segment be executed after the request is returned. This request will be returned after Ms. After that, other events may be triggered.
In the above text, each piece of code is an event-handler, and the time when event-handler is triggered may be affected by the previous event-handler. The execution time of each event-handler is very short. The following figure can be mentioned (the asynchronous function corresponding to event-hanlder is marked above, and the approximate time is indicated below ):

We can see the event execution sequence in the figure, which is easy to understand. Now, if the event-handler of a vertex event executes a while loop for 100 ms and then setInterval and ajax requests, what is the execution order? If you understand the javascript event-driven mechanism, this is easy. Leave a piece of code and try it on your own. Is it the same as everyone thinks?

Var obj = {"num": 1}, start = new Date;
SetTimeout (function () {obj. num = 2}, 0 );

While (new Date-start <1000 ){}

Alert (JSON. stringify (obj ));

We may also think of the reasons behind some of the problems we encounter:

Why is the execution interval of setInterval less than setTimeout in most cases?
Why is there a minimum interval for setTimeout? The HTML5 specifications of whatwg and w3c both stipulate 4 ms
Why is it recommended that time-consuming functions be executed multiple times? For example, process. nextTick.

Currently, there are three main asynchronous programming solutions:

PubSub mode (Distributed Event)
Promise object
Workflow control Library

Next we will analyze them one by one. Before these asynchronous schemes, we often see the so-called pyramid bad luck.

AsyncFunc1 (function (result1 ){
// Some codes
AsyncFunc2 (function (result2 ){
// Some codes
AsyncFunc3 (function (result3 ){
// Other codes
});
});
});

Then, our solution is to make it easier for us to organize asynchronous code and avoid problems like the above.

PubSub mode (Distributed Event)

The so-called PubSub mode is actually very simple. For example, the dom. addEventListener we usually use is a fresh example of the PubSub mode. Before DOM Level 2 was released in 2000, we may need to bind events in a way similar to dom. onclick. This can easily cause problems. If there is no distributed event, we cannot:

Dom. onclick = eventHandler1;
Dom. onclick = eventHandler2;

Obviously, onclick is only an attribute of dom. The same key cannot correspond to multiple values. The first key will be overwritten by the second key, so we can only:

Dom. onclick = function (){
EventHandler1.apply (this, arguments );
EventHandler1.apply (this, arguments );
};
`
There are many disadvantages, such as inflexible and lengthy code, which is not conducive to maintenance.

Now I start to learn the front-end, and there may be no teachers or books to explain such usage. After dom. addEventListener is standardized, we can:

Dom. addEventListener ('click', eventHandler1 );
Dom. addEventListener ('click', eventHandler2 );

A class library such as jquery naturally balances the differences between different browsers and provides a method similar to $ dom. on. Today, almost all front-end dom-related class libraries provide similar APIs. Of course, at the other end of the javascript world, nodejs also has the EventEmitter object provided by the core module Events to easily implement distributed Events:

Var Emitter = require ("events"). EevntEmitter;

Var emitter = new Emitter ();

Emitter. on ('someevent', function (stream ){
Console. log (stream + 'from eventhandler1 ');
});

Emitter. on ('someevent', function (stream ){
Console. log (stream + 'from eventhandler2 ');
});

Emitter. emit ('someevent', 'I am a stream! ');

The example of DOM does not show that the PubSub mode is event listening, but because event listening is a typical example of distributed events, our subscription and release rely on objects instead of a common object, it is also a browser DOM object, and in jQuery this object is a jQuery object. Below, we use simple code to implement a PubSub mode:

Var PubSub = {handler :{}};

PubSub. sub = function (evnt, handler ){
Var handlers = this. handlers;

! (Event in handlers) & handlers (event) = [];

Handers [event]. push (handler );
}

PubSub. pub = function (event ){
Var handlers = (handlers [event] | []);

Var handlerArgs = []. slice. call (arguments, 1 );

For (var I = 0, item; item = handlers [I]; I ++ ){
Item. apply (this, handlerArgs );
    }

Return this;
}

As we can see, the above code is just the simplest or even insecure implementation. There are many mature frameworks in the production environment, such as the implementation of the pure PubSub mode such as PubSubJS. At the same time, from the above implementation, we can find that all event-handler are executed synchronously, which is still different from the time when the event is actually clicked in our browser, the actual click event handler will be triggered in the subsequent event-loop. Similarly, we use the manual dom. click () or $ dom of jQuery. click () is executed synchronously (you can test it ).

The PubSub mode is the most commonly used method and is relatively easy to understand. Based on this kind of event-based object, code is implemented in different layers. This technology is also used by the famous Backbone. js. This is the benefit of the PubSub mode. However, the event is not a panacea. In some cases, it is not suitable for Event Processing. For example, some processes with one-time conversion and only success or failure results are not suitable for the PubSub mode. In this situation, Promise is more suitable for us.

Promise object

Promise has its own implementations in many languages, and its bond with javascript is thanks to the milestone Dojo framework in the history of javascript development. Inspired by the Dojo developer Twisted in 2007, a Dojo. Deferred object was added to dojo. In 2009, Kris Zyp proposed the Promiser/A specification in the CommmonJS community. After that, nodejs suddenly emerged (in early 2010, nodejs gave up its native support for Promise). In 2011, jQuery1.5 was born with a rebellious Promise implementation and a brand new ajax, since then, Promise has been well known by javascript developers.

Today, more implementations have long been concerned with the fuller Promise/A + specifications of the wings, and jQuery's implementation of Promise has also compromised the standards, like Q. the emergence of js also gives the javascript world an intuitive and pure implementation of both clients and servers.

In the near future (December 2014), there will be a significant moment in the history of javascript development, and ES6 will become a formal standard. Among the many eye-catching features, the native support for Promise is still eye-catching. If it is re-configured with Generator, it will be even more powerful.

In the future, ES7 will provide an async keyword-guided declaration function that supports await. How can we wait and see this pattern.

The Promise/A specifications in the CommonJS community are relatively simple, while the Promise/A + specifications supplement them. We will use Promise/A + specifications to learn Promise through instances later.

What is Promise? Promise is an object that represents the returned results of an asynchronous function. It is represented by code:

Var promise = asyncFunction ();

A common ajax call of jQuery is as follows:

Var ajaxPromise = $. ajax ('mydata ');
AjaxPromise. done (successFunction );
AjaxPromise. fail (errorFunction );
AjaxPromise. always (completeFunction );

From the code above, we can see that the Promise object returned by jQuery has several methods. For example, done, fail, and always correspond to ajax success, failure, and callback that should be executed regardless of success or failure, these methods can be seen as the syntactic sugar brought by the specific implementation above the specifications. So what is the real Promise specification? (In fact, the specifications are relatively short. You can take a moment to read them. Here we will introduce them as the backbone)

Promise can be in one of the following three states: pending, fulfilled, and rejected. Relationship between the three states:

Pending: can be changed to fulfilled or rejected status.
Fulfilled: cannot be changed to any other state, and there must be an unchangeable value
Rejected: it cannot be changed to any other state, and there must be an unchangeable reason.
For value and reason, we can understand the reason for fulfilled results and rejected respectively.

Promise must have a then method to access the current or final value or reason. The then method has two parameters, both of which are optional. Use promise. then (onFulfilled, onRejected ). The analysis is as follows:

OnFulfilled: ignored if it is not a function.
If it is a function, it is triggered only once after the promise State is converted to fulfilled, and only the value of promise is passed as the first parameter.

OnRejected: if it is not a function, it will be ignored.
If it is a function, it is triggered only once after the promise State is converted to rejected, and only the reason of promise is passed as the first parameter.

In addition, when you call the then-bound callback function multiple times, the execution sequence corresponds to the binding sequence when fulfilled or rejected is executed. The call must be executed in the event loop after then.
The then method of Promise must return a promise object for chained calling. If onFulfilled or onRejected has throw, the generated Promise object should convert the thrown content to reason to rejected state.

After analyzing the Promise specification, we can improve the first code in this chapter:

Var promise = asyncFunction ();
Promise = promise. then (onFulfilled1, onRejected1)
. Then (onFulfilled2, onRejected2 );

Promise. then (onFulfilled3, onRejected3 );
There are many implementations of Promise/A specifications. Which one should we choose in our actual production? This can only be said to suit local conditions.

Of course, there should be many people like me now, looking forward to ES6's native Promise implementation. Elasticsearch's standardized Promise looks like this:

Var promise = new Promise (function (resolve, reject ){
// Do a thing, possibly async, then...

If (/* everything turned out fine */){
Resolve ("Stuff worked! ");
  }
Else {
Reject (Error ("It broke "));
  }
});

Promise. then (function (result ){
Console. log (result); // "Stuff worked! "
}, Function (err ){
Console. log (err); // Error: "It broke"
});

Next, let's take a look at Generator. If you do not know what Generator is? Here. A concise description is that the Generator function can interrupt function execution with a specific yield keyword and share the execution context with the outside world. Based on this feature, the Generator function can work with the asynchronous function to wait for the execution (result) of the asynchronous function, and then use the specific interface (next) inject asynchronous results into Generator's own context, and then continue to execute the subsequent code.

In this way, we can write asynchronous code in synchronous mode. There are many implementations that can work with Generator, including Promise objects, and the express host TJ provides us with a very mature solution-co. Personally, optimizing asynchronous code based on Generator will be the most popular method in the future. I recommend a few excellent articles here, so that I will not be able to work hard.

Park Ling's hot Generator and asynchronous programming, I don't know which teacher's Harmony Generator, yield, ES6, co framework learning, Qu da ES6 Generator function introduction. If you want to learn about this technology, click the above link.

In addition, Google and Mozilla provide their own solutions: traceur and taskjs.

Based on Promise, we can implement various serial parallel asynchronous operations. However, this serial parallel control requires manual maintenance, while the flow-control solution, this satisfies our needs. Let's start from here.

Workflow control Library

The so-called flow control Library (flow control), I describe in my own language, is through the inherent mode (The Library provides related APIs) to organize the execution of tasks (code/function, this allows you to easily implement parallel serial. For example, if we have a requirement to read three files and the three files have sequential dependencies, what we need to do is sequential reading, the original code may be like this:

Fs. readFile ('originalfile', function (err, data1 ){
Fs. readFile (data1, function (err, data2 ){
Fs. readFile (data2, function (err, data3 ){
// Operate with data3
});
});
});

We see a "beautiful" pyramid. So what if I use the prestigious async?

Async. waterfall ([
Function (cb ){
Fs. readFile ('originalfile', cb );
}, Function (data1, cb ){
Fs. readFile (data1, cb );
}, Function (data2, cb ){
Fs. readFile (data2, cb );
  }
], Function (err, result ){
// Result now equals data3 & operate with data3
});

With the same requirement, how is the code implemented with a simple step?

Step (function (){
Fs. readFile ('originalfile', this );
}, Function (err, data1 ){
Fs. readFile (data1, this );
}, Function (err, data2 ){
Fs. readFile (data2, this );
}, Function (err, data3 ){
// Operate with data3
});

With regard to the original scheme, asynchronous functions nested asynchronous functions are not explained at a glance. Next, let's compare async and step:

The most obvious difference is that async exposes an object, and there are APIs under the object that implement certain processes. For example, in our needs, there is a sequential dependency between top and bottom. async will provide us with a very literary api called waterfall, and there is no dependency relationship but only a sequential requirement, we can use async. series. async can be used for parallel promotion tasks. parallel and so on.

Compared with async, step is much simpler. step provides only one function, which accepts a series of functions as parameters, different types of process control are implemented according to the call of this in the function. In the above example, the asynchronous function passes the result to this when the callback function of the step is executed (as you think, this is a function at this time ), it is through this that the results of asynchronous operations are passed into the callback function of the next step to implement process control. With this, we can implement other process control, such as requiring parallel tasks:

Step (function loadStuff (){
Fs. readFile ('file-1', this. parallel ());
Fs. readFile ('file-2', this. parallel ());
Fs. readFile ('file-3', this. parallel ());
}, Function showStuff (err, f1, f2, f3 ){
// Operate with f1, f2, f3
  }
);

In addition, async also provides some methods for easy-to-use set operations and some tool functions beyond process control. For example, for map operations similar to arrays, let's look at the following functions:

Async. map (['file1', 'file2', 'file3'], fs. stat, function (err, results ){
// Results is now an array of stats for each file
});

What should we do when we need to use step to implement similar requirements? Because step is minimalistic and the source code is a total of more than a hundred lines, we can use the existing functions and methods to simulate a stepMap:

Function stepMap (arr, iterator, callback ){
Step (function (){
Var group = this. group ();
For (var I = 0, l = arr. length; I <l; I ++ ){
Iterator (arr [I], group ());
    }
}, Callback );
}

In short, async and step are two common implementations of this type of solution. Which one is better? I think they have their own advantages and disadvantages, just as we weigh between express and connect. Async is a good choice if you like easy to use and are naturally sensitive to Apis. If you like me, you like conciseness, and you do not want to memorize APIs, try step.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.