As you may know, the execution environment of the Javascript language is "single thread".
The so-called "single thread" means that one task can only be completed at a time. If there are multiple tasks, you must queue up, complete the previous task, execute the next task, and so on.
The advantage of this model is that it is relatively simple to implement and the execution environment is relatively simple; the disadvantage is that as long as there is a task that takes a long time, the subsequent tasks must be queued up, which will delay the execution of the entire program. Common browsers are unresponsive (fake death) often because a certain piece of Javascript code runs for a long time (such as a dead loop), causing the entire page to be stuck in this place and other tasks cannot be executed.
To solve this problem, the Javascript language divides the execution mode of tasks into two types: Synchronous (Synchronous) and Asynchronous (Asynchronous).
"Synchronous mode" is the mode of the previous section, and the latter task waits for the previous task to end, and then executes it. The execution order of the program is consistent and synchronous with the arrangement of tasks; "Async mode" is completely different, and each task is There are one or more callback functions (callbacks). After the previous task is finished, the next task is not executed, but the callback functions are executed. The next task is executed without waiting for the previous task to end, so the execution order of the program is and the task. The order of arrangement is inconsistent and asynchronous.
"Async mode" is very important. On the browser side, long-term operations should be executed asynchronously to avoid the browser from losing response. The best example is Ajax operations. On the server side, "asynchronous mode" is even the only mode, because the execution environment is single-threaded, if all http requests are allowed to be executed synchronously, the server performance will drop sharply and will soon lose its response.
1. Callback function
This is the most basic method of asynchronous programming.
Suppose there are two functions f1 and f2, the latter waiting for the execution result of the former.
The code copy is as follows:
f1();
f2();
If f1 is a time-consuming task, you can consider rewriting f1 and writing f2 as the callback function of f1.
The code copy is as follows:
function f1(callback){
setTimeout(function () {
// F1's task code
callback();
}, 1000);
}
Execution code becomes like this:
f1(f2); In this way, we turn synchronous operations into asynchronous operations. F1 will not block the program's operation, which is equivalent to executing the main logic of the program first and postponing the execution of the time-consuming operation.
The advantage of callback functions is that they are simple, easy to understand and deploy, and the disadvantage is that they are not conducive to the reading and maintenance of code. They are highly coupled (coupling) between the various parts, and the process will be very chaotic, and each task can only specify one callback function.
2. Event monitoring
Another idea is to adopt an event-driven model. The execution of a task does not depend on the order of the code, but on whether an event occurs.
Let’s take f1 and f2 as examples. First, bind an event for f1 (the jQuery writing method used here).
The code copy is as follows:
f1.on('done', f2);
The above line of code means that when the done event occurs in f1, f2 is executed. Then, rewrite f1:
The code copy is as follows:
function f1(){
setTimeout(function () {
// F1's task code
f1.trigger('done');
}, 1000);
}
f1.trigger('done') means that after the execution is completed, the done event is triggered immediately, thereby starting to execute f2.
The advantage of this method is that it is relatively easy to understand. It can bind multiple events, and each event can specify multiple callback functions, and it can be "decoupling", which is conducive to modularization. The disadvantage is that the entire program will become event-driven, and the operation process will become very unclear.
3. Publish/Subscribe
The "event" in the previous section can be fully understood as a "signal".
We assume that there is a "signal center". If a task is completed, a signal will be published to the signal center. Other tasks can "subscribe" the signal to the signal center, so as to know when you can Start execution. This is called "publish-subscribe pattern", also known as "observer pattern".
There are many implementations of this model. The following is Ben Alman's Tiny Pub/Sub, which is a plug-in for jQuery.
First, f2 subscribes to the "done" signal to the "signal center" jQuery.
The code copy is as follows:
jQuery.subscribe("done", f2);
Then, f1 is rewritten as follows:
The code copy is as follows:
function f1(){
setTimeout(function () {
// F1's task code
jQuery.publish("done");
}, 1000);
}
jQuery.publish("done") means that after f1 execution is completed, a "done" signal is issued to the "signal center" jQuery, thereby triggering the execution of f2.
In addition, after f2 is executed, unsubscribe can also be unsubscribed.
The code copy is as follows:
jQuery.unsubscribe("done", f2);
The nature of this approach is similar to "event listening", but is significantly better than the latter. Because we can monitor the operation of the program by looking at the "Message Center" to understand how many signals exist and how many subscribers are for each signal.
4. Promises object
The Promises object is a specification proposed by the CommonJS working group, with the purpose of providing a unified interface for asynchronous programming.
Simply put, its idea is that each asynchronous task returns a Promise object, which has an then method that allows the callback function to be specified. For example, the callback function f2 of f1 can be written as:
The code copy is as follows:
f1().then(f2);
f1 needs to be rewritten as follows (the implementation of jQuery is used here):
The code copy is as follows:
function f1(){
var dfd = $.Deferred();
setTimeout(function () {
// F1's task code
dfd.resolve();
}, 500);
return dfd.promise;
}
The advantage of writing this way is that the callback function has become a chain writing method, and the program flow can be seen very clearly, and there is a complete set of supporting methods that can realize many powerful functions.
For example, specify multiple callback functions:
f1().then(f2).then(f3);
For example, specify the callback function when an error occurs:
f1().then(f2).fail(f3);
Moreover, it has an advantage that none of the previous three methods have: if a task has been completed, add a callback function, and the callback function will be executed immediately. So, you don't have to worry about missing an event or signal. The disadvantage of this method is that it is relatively difficult to write and understand.