During development, there are many requests to be made, and then there is often the problem of nested callbacks, where a callback is nested within a callback, resulting in a layer of indented code, as shown in the following code:
ajax({
url: "/list".type: "GET".success: function(data) {
appendToDOM(data);
ajax({
url: "/update".type: "POST".success: function(data) {
util.toast("Success!"); })}); }});Copy the code
This kind of code seems a bit cumbersome, and the asynchronous callback can often be optimized with Promise, which can be changed to:
new Promise(resolve= > {
ajax({
url: "/list".type: "GET".success: data= > resolve(data);
})
}).then(data= > {
appendToDOM(data);
ajax({
url: "/update".type: "POST".success: function(data) {
util.toast("Successfully!"); })}); });Copy the code
Promise provides a resolve to tell when asynchrony ends, but the essence is the same: the callback is placed inside the THEN.
When you need to fetch asynchronous data multiple times, you can use promise.all:
let orderPromise = new Promise(resolve= > {
ajax("/order"."GET", data => resolve(data));
});
let userPromise = new Promise(resolve= > {
ajax("/user"."GET", data => resolve(data));
});
Promise.all([orderPromise, userPromise]).then(values= > {
let order = values[0],
user = values[1];
});Copy the code
But this is also using a callback, is there a more elegant way to do it?
ES7 await/async allows asynchronous callbacks to be written in the same way as synchronous code. The first example of a nested callback can be changed to the following code with await:
// Get asynchronous data with await
let leadList = await new Promise(resolve= > {
ajax({
url: "/list".type: "GET".success: data= > resolve(data);
});
});
// await makes code write naturally like a waterfall
appendToDom(leadList);
ajax({
url: "/update".type: "POST".success: (a)= > util.toast("Successfully");
});
Copy the code
Await allows code to be written naturally like waterfall flow.
Second example: get asynchronous data multiple times, can be changed like this:
let order = await new Promise(
resolve= > ajax("/order", data => resovle(data))),
user = await new Promise(
resolve= > ajax("/user", data => resolve(data)));
// do sth. with order/userCopy the code
This is written as if the data is fetched locally, without the need for the callback function.
“Await” is not only used for making requests, but also for other asynchronous scenarios. For example, before creating an order, I first pop up a small box to ask the user what type of order to create, and then pop up a specific box to set the order. Therefore, according to normal thinking, I need to pass a button callback click function, as shown in the figure below:
But you can use await, as shown in the following code:
let quoteHandler = require("./quote");
// A pop-up box asks the user and gets the user's choice
let createType = await quoteHandler.confirmCreate();Copy the code
Quote returns a Promise, listens for the click event, and passes createType:
let quoteHandler = {
confirmCreate: function(){
dialog.showDialog({
contentTpl: tpl,
className: "confirm-create-quote"
});
let $quoteDialog = $(".confirm-create-quote form") [0];
return new Promise(resolve= > {
$(form.submit).on("click".function(event){ resolve(form.createType.value); }); }); }}Copy the code
This way the external caller can use await instead of passing a callback function for the click event.
But note the one-off execution nature of await. Relative to the callback function, await execution is one-off, such as listening to the click event, and then use the await, then click on the event will only once, because the code from the top down and performed, so when you want to click the wrong also can continue to modify and submitted will not be able to use await, in addition to use await for asynchronous data, If an error occurs, the successful resolve will not be executed, nor will any subsequent code, so the basic logic will be fine when the request fails.
To use await inside Babel, you need:
(1) Install a Node package
npm install –save-dev babel-plugin-transform-async-to-generator
(2) Add a.babelrc file to the root directory of the project.
{
"plugins": ["transform-async-to-generator"]}Copy the code
(3) Introduce a module first when using
require("babel-polyfill");Copy the code
Then you can happily use the AWAIT of ES7.
Functions that use await should be preceded by async keyword as follows:
async showOrderDialog() {
// Get the creation type
let createType = await quoteHandler.confirmCreate();
// Get old order data
let orderInfo = await orderHandler.getOrderData();
}
Copy the code
We will give you an example: use await implement JS version of the sleep function, because native is not provided for in the thread to sleep function, as shown in the following code:
function sleep (time) {
return new Promise(resolve= >
setTimeout((a)= > resolve(), time));
}
async function start () {
await sleep(1000);
}
start();
Copy the code
The “await” implementation of Babel is converted to an ES6 generator with the following key code:
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
Sleep returns a Promise object
return sleep(1000);
case 2:
case "end":
return_context.stop(); }}Copy the code
Babel’s generator is also implemented in ES5. What is a generator? As shown below:
The generator is defined as function*. Each time a generator’s next function is executed, it returns the yield value in the current generator, and the generator iterator takes a step back until all yields are exhausted.
For those of you interested, see how Babel converted ES7 to ES5, and the native implementation is said to be directly based on Promise.
An additional benefit of using await is that we can try and catch an exception thrown by an asynchronous procedure directly, because we cannot catch an exception inside an asynchronous callback directly.
let quoteHandler = {
confirmCreate: function(){
$(form.submit).on("click".function(event){
// undefined is thrown: the value of undefined is accessedcallback(form.notFoundInput.value); }); }}try {
// No exception can be caught here
quoteHandler.confirmCreate();
} catch (e) {
}Copy the code
There is no way to catch an exception in a try-catch, because the code in a try has run out and there is no exception in its execution, so it cannot catch an exception here. If you use a Promise, you usually use a catch in the Promise chain:
let quoteHandler = {
confirmCreate: function(){
return new Promise(resolve= > {
$(form.submit).on("click".function(event){
// undefined is thrown: the value of undefined is accessed
resolve(form.notFoundInput.value);
});
});
}
}
quoteHandler.confirmCreate().then(createType= > {
}).catch(e= > {
// Exceptions can be caught here
});Copy the code
And with await, we can just use synchronous catch as if it really became synchronous execution:
try {
createType = await quoteHandler.confirmCreate("order");
}catch(e){
console.log(e);
return;
}Copy the code
In short, using await makes code less nested, very convenient logical processing, and enjoy smooth.