Async functions, a feature in ES2017, make async code look sync by using promises (a particular form of async code) and the await keyword. Also notice in the code examples below the keyword async in front of the function keyword that signifies an async/await function. The await keyword won't work without being in a function pre-fixed with the async keyword. Since currently there is no exception to this that means no top level awaits will work (top level awaits meaning an await outside of any function). Though there is a proposal for top-level await.
ES2017于2017年6月27日被批准(即最终确定)为JavaScript标准。Async await可能已经在你的浏览器中工作了,但如果不是,你仍然可以使用像babel或traceur这样的javascript转译器使用该功能。Chrome 55完全支持异步功能。因此,如果您有一个较新的浏览器,您可以尝试下面的代码。
浏览器兼容性参见kangax的es2017兼容性表。
下面是一个名为doAsync的示例async await函数,它接受三次1秒暂停,并在每次暂停后打印与开始时间的时间差:
function timeoutPromise (time) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(Date.now());
}, time)
})
}
function doSomethingAsync () {
return timeoutPromise(1000);
}
async function doAsync () {
var start = Date.now(), time;
console.log(0);
time = await doSomethingAsync();
console.log(time - start);
time = await doSomethingAsync();
console.log(time - start);
time = await doSomethingAsync();
console.log(time - start);
}
doAsync();
当await关键字被放置在promise值之前(在这种情况下,promise值是由函数doSomethingAsync返回的值),await关键字将暂停函数调用的执行,但它不会暂停任何其他函数,它将继续执行其他代码,直到promise解决。在promise解决后,它将打开promise的值,您可以认为await和promise表达式现在已被已打开的值所取代。
因此,由于await只是暂停等待,然后在执行行剩余部分之前展开一个值,您可以在for循环和内部函数调用中使用它,就像下面的示例一样,收集在数组中等待的时间差并打印出数组。
function timeoutPromise (time) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(Date.now());
}, time)
})
}
function doSomethingAsync () {
return timeoutPromise(1000);
}
// this calls each promise returning function one after the other
async function doAsync () {
var response = [];
var start = Date.now();
// each index is a promise returning function
var promiseFuncs= [doSomethingAsync, doSomethingAsync, doSomethingAsync];
for(var i = 0; i < promiseFuncs.length; ++i) {
var promiseFunc = promiseFuncs[i];
response.push(await promiseFunc() - start);
console.log(response);
}
// do something with response which is an array of values that were from resolved promises.
return response
}
doAsync().then(function (response) {
console.log(response)
})
async函数本身返回一个promise,所以你可以像我上面所做的那样,在另一个async await函数中使用它作为链接的promise。
上面的函数会在发送另一个请求之前等待每个响应,如果你想并发发送请求,你可以使用Promise.all。
// no change
function timeoutPromise (time) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(Date.now());
}, time)
})
}
// no change
function doSomethingAsync () {
return timeoutPromise(1000);
}
// this function calls the async promise returning functions all at around the same time
async function doAsync () {
var start = Date.now();
// we are now using promise all to await all promises to settle
var responses = await Promise.all([doSomethingAsync(), doSomethingAsync(), doSomethingAsync()]);
return responses.map(x=>x-start);
}
// no change
doAsync().then(function (response) {
console.log(response)
})
如果promise可能被拒绝,你可以将它包装在try catch中,或者跳过try catch,让错误传播到async/await函数的catch调用。你应该注意不要让承诺错误未得到处理,尤其是在Node.js中。下面是一些演示错误如何工作的示例。
function timeoutReject (time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
reject(new Error("OOPS well you got an error at TIMESTAMP: " + Date.now()));
}, time)
})
}
function doErrorAsync () {
return timeoutReject(1000);
}
var log = (...args)=>console.log(...args);
var logErr = (...args)=>console.error(...args);
async function unpropogatedError () {
// promise is not awaited or returned so it does not propogate the error
doErrorAsync();
return "finished unpropogatedError successfully";
}
unpropogatedError().then(log).catch(logErr)
async function handledError () {
var start = Date.now();
try {
console.log((await doErrorAsync()) - start);
console.log("past error");
} catch (e) {
console.log("in catch we handled the error");
}
return "finished handledError successfully";
}
handledError().then(log).catch(logErr)
// example of how error propogates to chained catch method
async function propogatedError () {
var start = Date.now();
var time = await doErrorAsync() - start;
console.log(time - start);
return "finished propogatedError successfully";
}
// this is what prints propogatedError's error.
propogatedError().then(log).catch(logErr)
如果你去这里,你可以看到即将到来的ECMAScript版本的完成建议。
An alternative to this that can be used with just ES2015 (ES6) is to use a special function which wraps a generator function. Generator functions have a yield keyword which may be used to replicate the await keyword with a surrounding function. The yield keyword and generator function are a lot more general purpose and can do many more things then just what the async await function does. If you want a generator function wrapper that can be used to replicate async await I would check out co.js. By the way co's function much like async await functions return a promise. Honestly though at this point browser compatibility is about the same for both generator functions and async functions so if you just want the async await functionality you should use Async functions without co.js.
(I recommend just using async/await it's pretty widely supported in most environments that the above strikethrough is supported in.)
目前除了IE之外,所有主流浏览器(Chrome、Safari和Edge)对Async函数的支持实际上都很好(截至2017年)。