异步函数是 ES2017 中的一项功能,它通过使用 promise(异步代码的特定形式)和关键字使异步代码看起来同步。另请注意,在下面的代码示例中,关键字前面是表示异步/等待函数的关键字。如果不在使用关键字预先修复的函数中,关键字将无法工作。由于目前没有例外,这意味着没有顶级等待将起作用(顶级等待意味着任何函数之外的等待)。虽然有一个关于顶级等待
的建议。await
async
function
await
async
ES2017 于 2017 年 6 月 27 日被批准(即最终确定)为 JavaScript 标准。Async await可能已经在您的浏览器中工作,但如果没有,您仍然可以使用javascript转译器(如babel或tracer)来使用该功能。Chrome 55完全支持异步功能。因此,如果您有较新的浏览器,则可以尝试以下代码。
请参阅 kangax 的 es2017 兼容性表,了解浏览器兼容性。
下面是一个调用的异步等待函数示例,该函数需要三个一秒的暂停,并在每次暂停后从开始时间开始打印时差:doAsync
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 解析为止。在承诺解析后,它将解开承诺的值,您可以将 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,因此您可以将其用作带有链接的 promise,就像我在上面或在另一个 async await 函数中所做的那样。
上面的函数将在发送另一个请求之前等待每个响应,如果您想同时发送请求,则可以使用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 版本的已完成建议。
可以与ES2015(ES6)一起使用的替代方法是使用一个包裹生成器函数的特殊函数。生成器函数具有 yield 关键字,可用于复制具有周围函数的 await 关键字。yield 关键字和生成器函数的用途要通用得多,可以做更多的事情,而不仅仅是异步 await 函数的功能。如果你想要一个可用于复制异步等待的生成器函数包装器,我会检查一下co.js。顺便说一句,co 的函数很像 async await 函数返回一个 promise。老实说,尽管在这一点上,生成器函数和异步函数的浏览器兼容性大致相同,因此,如果您只想要异步等待功能,则应使用异步函数而不使用.js。(我建议只使用 async/await,在大多数支持上述删除线的环境中,它得到了非常广泛的支持。
现在,除了IE之外,对于所有主要当前浏览器(Chrome,Safari和Edge)中的异步功能(截至2017年),浏览器支持实际上非常好。