在很长一段时间里,JavaScript 开发人员不得不依赖回调来处理异步代码。因此,我们中的许多人都经历过回调地狱,当面对这样的函数时,我们会感到无赖
不过好在 JavaScript 提供了,.then()的一种回复方式,目前也有很多人正在用他
现在,随着 Async/Await 的最新增加,编写 JavaScript 代码将会变得更加优雅!
什么是 Async/Await?
Async/Await 是一个期待已久的 JavaScript 特性,它使得使用异步函数变得更加愉快和更容易理解。它建立在 Promises 之上,并与所有现有的基于 promise 的 api 兼容
这个名字来自 async 和 await ——这两个关键词可以帮助我们清理异步代码:
Async- 声明一个异步函数(Async function someName (){…})
- 自动地将一个普通的函数转换成一个承诺
- 当调用异步函数时,当异步完成时,然后返回的是异步内容
- 在异步函数中允许使用 await
Await- 暂停 async 函数的执行
- 当被放置在一个 Promise 前时, 强制代码的其余部分等待,直到承诺完成并返回结果
- 只对 Promises 起作用,对 callback 不起作用
- 只能在里面使用 async 函数中使用
通过一个简单的例子来分析
假设我们想从服务器获取一些 JSON 文件。我们将编写一个使用 axios 库的函数,并向 https://tutorialzine.com/misc… 发送一个 HTTP GET 请求。我们必须等待服务器响应,所以这个 HTTP 请求自然是异步的
下面我们可以看到同一个函数实现了两个。首先是使用 Promises,然后是第二个使用 Async/Await
// Promise
function getJSON(){
// 为了使函数阻塞,我们手动创建一个承诺.
return new Promise(function(resolve) {axios.get('https://tutorialzine.com/misc/files/example.json')
.then(function(json) {
// 在.then 中获得返回的 json 数据
// 我们使用 resolve 返回结果
resolve(json);
});
});
}
// Async/Await
// async 关键字将自动创建一个新的 Promise 并返回它.
async function getJSONAsync(){// await 关键字使我们不必编写 then().
let json = await axios.get('https://tutorialzine.com/misc/files/example.json');
// GET 请求的结果在 json 变量中可用.
// 获取数据就像在一个常规的同步函数中一样
return json;
}
很明显,Async/Await 版本的代码更短,更容易阅读。除了使用的语法之外,这两个函数完全相同——它们都返回 Promises 并使用 axios 的 JSON 响应解析。我们可以这样调用我们的 async 函数:
async 返回本身就是一个 Promise
getJSONAsync().then( function(result) {// Do something with result.});
Async/Await 会使承诺过时吗?
不,一点也不。
在使用 Async/Await 时,我们仍然在引擎盖下使用 Promises。
从长远来看,充分理解 Promise
实际上会对你有所帮助,因此强烈推荐你这么做
甚至在一些情况下 Async/Await 也不能解决问题,我们不得不回到 Promises 寻求帮助。其中一种情况是,我们需要进行多个独立的异步调用,并等待它们全部完成
如果我们尝试使用 async 和 await 来实现这个功能,将会发生以下情况:
async function getABC() {let A = await getValueA(); // getValueA 需要 2 秒
let B = await getValueB(); // getValueB 需要 4 秒
let C = await getValueC(); // getValueC 需要 3 秒
return A*B*C;
}
每个 await 等待都将等待 await 前一个返回结果。因为我们一次只调用一个函数,整个函数从开始到结束 (2 + 4 + 3) 需要 9 秒
这不是最佳解决方案,因为三个变量 a、b 和 c 并不相互依赖。换句话说,在得到 b 之前,我们不需要知道 a 的值。我们可以在同一时间得到它们,减少几秒钟的等待时间
在同一时间发送所有请求。这将确保我们在继续之前仍然拥有所有的结果,但是异步调用将并行请求,而不是一个接一个地请求
async function getABC() {// Promise.all()允许我们同时发送多个请求,类型是个数组
let results = await Promise.all([getValueA, getValueB, getValueC]);
return results.reduce((total,value) => total * value);
}
这样一来,函数运行的时间就会少得多。getValueA 和 getValueC 调用在 getValueB 结束时已经完成,将有效地将执行时间减少到最慢的请求的时间(getValueB- 4 秒),而不是总和
Async/Await 中的错误处理
Async/Await 的另一个优点是,它允许我们在一个很好在 try/catch 块中捕捉任何意外的错误。只需要像这样包装下 Await:
async function doSomethingAsync(){
try {
// 这里异步可能会发送失败
let result = await someAsyncCall();}
catch(error) {// 如果失败了,通过 catch 捕捉到错误}
}
Catch 子句将处理等待的异步调用或者我们在 try 块中编写的任何其他失败代码所引发的错误
如果情况需要,我们还可以在执行 async 函数时捕获错误。因为所有的异步函数都返回 Promises,我们可以简单地包含一个。在调用 catch ()事件处理程序时。
async function doSomethingAsync(){
// This async call may fail.
let result = await someAsyncCall();
return result;
}
// 后面更用 catch 捕获错误
doSomethingAsync().
.then(successHandler)
.catch(errorHandler);
总结
随着 Async/Await 的加入,JavaScript 语言在代码可读性和易用性方面取得了巨大的飞跃。编写类似于常规同步函数的异步代码的能力将受到初学者、JavaScript 开发者和资深编码者的青睐
文章属于翻译,作者:羊先生
英文原文