Currently, a large number of asynchronous operations are involved in the demand, and actual pages are increasingly inclined to single-page applications. In the future, you can use backbone, angular, knockout and other frameworks, but the problem of asynchronous programming is the first problem to be faced. With the rise of nodes, asynchronous programming has become a very hot topic. After a period of learning and practice, some details of asynchronous programming are summarized.
1. Classification of asynchronous programming
The methods to solve the asynchronous problem generally include: direct callback, pub/sub mode (event mode), asynchronous library control library (such as async, when), promise, Generator, etc.
1.1 Callback Function
Callback functions are commonly used to solve asynchronous solutions, often exposed and used, easy to understand, and very easy to implement in libraries or functions. This is also a method that people often use when using asynchronous programming.
However, the callback function method has the following problems:
1. A nested pyramid of evil may be formed, and the code is not easy to read;
2. Only one callback function can be corresponding, which becomes a limit in many scenarios.
1.2 pub/sub mode (event)
This pattern is also called event mode, which is eventization of callback functions and is very common in class libraries such as jQuery.
The event publish subscriber mode itself does not have the problem of synchronous and asynchronous calls, but in node, emit calls are mostly triggered asynchronously with the event loop. This mode is often used to decouple business logic. Event publishers do not need to pay attention to the registered callback function, nor do they need to pay attention to the number of callback functions. Data can be transmitted flexibly through messages.
The benefits of this pattern are: 1. Easy to understand; 2. No longer limited to one callback function.
When it comes to bad things: 1. You need to use the class library; 2. The order of events and callback functions is very important
The code copy is as follows:
var img = document.querySelect(#id);
img.addEventListener('load', function() {
// The picture is loaded
......
});
img.addEventListener('error', function() {
// Something went wrong
......
});
There are two problems with the above code:
a. img has actually been loaded, and the load callback function is bound at this time. As a result, the callback will not be executed, but it still hopes to execute the corresponding callback function.
The code copy is as follows:
var img = document.querySelect(#id);
function load() {
...
}
if(img.complete) {
load();
} else {
img.addEventListener('load', load);
}
img.addEventListener('error', function() {
// Something went wrong
......
});
b. Unable to handle exceptions well
Conclusion: The event mechanism is best suited to deal with repeated events on the same object, and there is no need to consider the event before the callback function is bound.
1.3 Asynchronous Control Library
The current asynchronous libraries mainly include Q, when.js, win.js, RSVP.js, etc.
The characteristic of these libraries is that the code is linear and can be written from top to bottom, in line with natural habits.
The bad things are also different in styles, which are inconvenient to read and increase learning costs.
1.4 Promise
Promise is translated into Chinese as a promise. Personally, after completion asynchronously, it will give an external result (success or failure) and promise that the result will not change anymore. In other words, Promise reflects the final return result value of an operation (A promise represents the eventual value returned from the single completion of an operation). At present, Promise has been introduced into the ES6 specification, and advanced browsers such as Chrome and firefox have implemented this native method internally, which is quite convenient to use.
The following are the characteristics of Promise from the following aspects:
1.4.1 Status
It contains three states: pending, fulfilled, and rejected. The three states can only undergo two transitions (from pending--->fulfilled, pending->rejected), and the transition of the state can only occur once.
1.4.2 then method
The then method is used to specify the callback function after the asynchronous event is completed.
This method can be said to be the soul method of Promise, which makes Promise full of magic. There are several specific manifestations as follows:
a) then method returns Promise. This enables serial operations of multiple asynchronous operations.
Regarding the yellow circle 1 in the above figure, the processing of value is a more complicated part of the Promise. The processing of value is divided into two situations: Promise object and non-Promise object.
When the value is not a Promise type, just use the value as the parameter value of the resolve of the second Promise; when it is a Promise type, the status and parameters of promise2 are completely determined by the value. It can be considered that promsie2 is a puppet of value, and promise2 is just a bridge connecting different asynchronous.
The code copy is as follows:
Promise.prototype.then = function(onFulfilled, onRejected) {
return new Promise(function(resolve, reject) { //The Promise here is marked as promise2
handle({
onFulfilled: onFulfilled,
onRejected: onRejected,
resolve: resolve,
reject: reject
})
});
}
function handle(deferred) {
var handleFn;
if(state === 'fulfilled') {
handleFn = deferred.onFulfilled;
} else if(state === 'rejected') {
handleFn = deferred.onRejected;
}
var ret = handleFn(value);
deferred.resolve(ret); //Note that the resolve at this time is the resolve of promise2
}
function resolve(val) {
if(val && typeof val.then === 'function') {
val.then(resolve); // If val is a promise object or a class promise object, the state of promise2 is completely determined by val
return;
}
if(callback) { // callback is the specified callback function
callback(val);
}
}
b) Conversion between multiple different asynchronous libraries is implemented.
There is an object called thenable in asynchronous, which refers to an object with the then method. As long as an object object has the then method, it can be converted, for example:
The code copy is as follows:
var deferred = $('aa.ajax'); // !!deferred.then === true
var P = Promise.resolve(deferred);
p.then(......)
1.4.3 commonJS Promise/A specification
Currently, there are Promise/A and Promise/A+ specifications for the specifications regarding the Promise, which shows that the implementation of Promise is quite complicated.
The code copy is as follows:
Then(fulfilledHandler, rejectedHandler, progressHandler)
1.4.4 Notes
The callback function in a Promise shares the value. In the result processing, the value is passed as a parameter to the corresponding callback function. If the value is an object, be careful not to easily modify the value.
The code copy is as follows:
var p = Promise.resolve({x: 1});
p.then(function(val) {
console.log('first callback: ' + val.x++);
});
p.then(function(val) {
console.log('second callback: ' + val.x)
})
// first callback: 1
// second callback: 2
1.5 Generator
All the above methods are based on the callback function to complete asynchronous operations, and they are nothing more than encapsulating the callback function. ES6 proposes Generator, which adds ways to solve asynchronous operations and is no longer completed based on callback functions.
The biggest feature of Generator is that it can pause and restart functions, which is very conducive to solving asynchronous operations. Combining Generator's pause with promise's exception handling can solve the asynchronous programming problem more elegantly. Specific implementation reference: Kyle Simpson
2. Problems with asynchronous programming
2.1 Exception handling
a) Asynchronous events include two links: issuing asynchronous requests and processing results. These two links are connected through event loops. Then when try catch to perform exception capture, you need to capture it.
The code copy is as follows:
try {
asyncEvent(callback);
} catch(err) {
......
}
The above code cannot catch the exception in the callback, and can only obtain the exception in the request process. This has problems: if the request issuance and the request processing are completed by two people, then there are problems when handling exceptions?
b) Promise implements exception delivery, which brings some benefits to ensure that the code is not blocked in actual projects. However, if there are many asynchronous events, it is not easy to find out which asynchronous event produces an exception.
The code copy is as follows:
// Scene description: Display price alarm information in CRM, including competitive information. However, it takes a long time to obtain the competitive information. In order to avoid slow querying, the backend splits a record into two pieces to obtain it separately.
// Step 1: Obtain price alarm information, in addition to competition information
function getPriceAlarmData() {
return new Promise(function(resolve) {
Y.io(url, {
method: 'get',
data: params,
on: function() {
success: function(id, data) {
resolve(alarmData);
}
}
});
});
}
// After obtaining the alarm information, go to obtain the competition information
getPriceAlarmData().then(function(data) {
// Data rendering, in addition to competitive information
render(data);
return new Promise(function(resolve) {
Y.io(url, {
method: 'get',
data: {alarmList: data},
on: function() {
success: function(id, compData) {
resolve(compData);
}
}
});
});
}) // After obtaining all data, rendering of the competition information
.then(function(data) {
// Render the bidding information
render(data)
}, function(err) {
// Exception handling
console.log(err);
});
The above code can be converted to the following:
The code copy is as follows:
try{
// Obtain alarm information other than competition
var alarmData = alarmDataExceptCompare();
render(alarmData);
// Inquiry of competition information based on alarm information
var compareData = getCompareInfo(alarmData);
render(compareData);
} catch(err) {
console.log(err.message);
}
In the above example, the exception is handled at the end, so that when an exception occurs in a certain link, we cannot know exactly which event is generated.
2.2 jQuery.Deferred problem
Asynchronous operations are also implemented in jQuery, but they do not comply with promise/A+ specifications in implementation, and are mainly reflected in the following aspects:
a. Number of parameters: Standard Promise can only accept one parameter, while jQuery can pass multiple parameters
The code copy is as follows:
function asyncInJQuery() {
var d = new $.Deferred();
setTimeout(function() {
d.resolve(1, 2);
}, 100);
return d.promise()
}
asyncInJQuery().then(function(val1, val2) {
console.log('output: ', val1, val2);
});
// output: 1 2
b. Handling of exceptions in result processing
The code copy is as follows:
function asyncInPromise() {
return new Promise(function(resolve) {
setTimeout(function() {
var jsonStr = '{"name": "mt}';
resolve(jsonStr);
}, 100);
});
}
asyncInPromise().then(function(val) {
var d = JSON.parse(val);
console.log(d.name);
}).then(null, function(err) {
console.log('show error: ' + err.message);
});
// show error: Unexpected end of input
function asyncInJQuery() {
var d = new $.Deferred();
setTimeout(function() {
var jsonStr = '{"name": "mt}';
d.resolve(jsonStr);
}, 100);
return d.promise()
}
asyncInJQuery().then(function(val) {
var d = JSON.parse(val);
console.log(d.name);
}).then(function(v) {
console.log('success: ', v.name);
}, function(err){
console.log('show error: ' + err.message);
});
//Uncaught SyntaxError: Unexpected end of input
It can be seen from this that Promise processes the result of the callback function, which can capture exceptions during the execution of the callback function, but jQuery.Deferred cannot.