什么是 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 开发者和资深编码者的青睐