10 分钟学会 js 的 Async/Await【翻译】
原文链接:https://tutorialzine.com/2017/07/javascript-async-await-explained
很长一段时间以来,js 开发者不得不依赖于回调方法来组织异步代码。以至于我们都曾饱受回调地狱之苦,时不时还会回忆起被代码支配的恐惧。
谢天谢地,Promises 方法为我们提供了更多组织异步代码的姿势,如今极大多数开发者都采用了 Promise 来避免回调地域的折磨。
随着 Async/Awai 的出现,我们能把异步代码写的更优雅。
Async/Await 究竟是啥
Async/Awai 一种被期待已久的优雅写法,这种写法会让你的异步代码写起来更舒服也更加容易被阅读。它的底层依然使用了 Promise,并且能和所有基于 Promise 的 api 兼容。
async
和 await
这两个关键字将会帮助我们厘清异步代码。
Async -声明了一个异步函数( async function someName(){...}
)
- 自动的将一个普通函数转化为 Promise
- 当调用异步函数时,函数返回值会被 resolve 处理
- 异步函数函数可以使用
await
Await -可以暂停异步函数的执行(var result = await someAsyncCall();)
- 在 Promise 前使用,
await
的代码将会在 Promise 执行完并返回结果之后再执行 await
只能和 Promise 一起使用,不能和 callback 一起使用await
只能用在async
函数中
下面是一个有助于帮助理解的小例子:
任务:从服务器获取一个json文件
为了完成这个需求,我们必须等待服务器响应之后再获取数据,所以需要使用异步函数
以下代码使用了两种方式来实现,借用了 axios
// Promise 写法
function getJSON(){
// 创建一个 Promise
return new Promise( function(resolve) {
axios.get('https://tutorialzine.com/misc/files/example.json')
.then( function(json) {
//.then 中可以获取到请求的返回值
// 我们使用 resolve 返回结果
resolve(json);
});
});
}
//Async/Await 写法
//`async`关键字会自动创建一个Promise并返回
async function getJSONAsync(){
// `await`关键字可让我们不用去写 then 上下文
// 下句的赋值语句会等 await 后面的内容执行完,在执行赋值
let json = await axios.get('https://tutorialzine.com/misc/files/example.json');
// json 变量会获得 get 请求的返回值
// 我们像同步方法那样直接返回 json 变量即可
return json;
}
很显然,用 Async/Await 组织代码更简短易读, 除了写法以外,功能上是完全一模一样的:都会获取到 Promise 的返回值,然后在后续步骤处理该值。我们可以这样调用上文的异步代码
getJSONAsync().then(function(result){
//do something
})
那么,Async/Await 能完全替代 Promise 吗
答案是不能。Async/Await 的底层仍在使用 Promise。从长远来看,完全理解 Promise 对你会有很大帮助,并且我强烈推荐大家好好研究一下 Promise。
有些场景下,我们不希望每个异步方法都阻碍整个方法的执行,比如来看下面这例子
async function getABC() {
let A = await getValueA(); // getValueA takes 2 second to finish
let B = await getValueB(); // getValueB takes 4 second to finish
let C = await getValueC(); // getValueC takes 3 second to finish
return A*B*C;
}
每一个 await 后面的方法都会打断 getABC()
执行,直到自身返回结果,这样一来getABC()就需要 2+4+3 = 9 秒才能执行完毕
这并不是我们希望的,因为 getValueA()
getValueB()
getValueC()
是相互独立的,我们希望他们能同步执行,这样一来 getABC()
就只需要花费 max(2,4,3) = 4 秒。
为了满足这样的需求,我们使用 Promise.all() 这样就可以让这些异步方法并行而不是一个一个执行。
async function getABC() {
// Promise.all() allows us to send all requests at the same time.
let results = await Promise.all([ getValueA, getValueB, getValueC ]);
return results.reduce((total,value) => total * value);
}
如此一来,我么就能节省不少时间,在 getValueB()
完成之前,getValueA()
和 getValueC()
就已经完成,一样我们就只花了 4 秒而不是 9 秒
Async/Await 的异常处理
Async/Await 的另一个好处是我们可以直接用 try/catch 进行处理,像这样:
async function doSomethingAsync(){
try {
// This async call may fail.
let result = await someAsyncCall();
}
catch(error) {
// If it does we will catch the error here.
}
}
如果需要的话,我们还可以在执行异步方法的时候捕获错误,因为当我们调用异步方法的时候,可以给返回的 Promise 增加一个 .catch 句柄
// Async function without a try/catch block.
async function doSomethingAsync(){
// This async call may fail.
let result = await someAsyncCall();
return result;
}
// We catch the error upon calling the function.
doSomethingAsync().
.then(successHandler)
.catch(errorHandler);
选择哪一种方法去抛出异常是很重要的,你可以选择你喜欢的并坚定地使用这种方式,同时使用 try/catch and .catch() 可能会导致一些意料之外的问题。