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 兼容。
asyncawait这两个关键字将会帮助我们厘清异步代码。
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
    })    

1 分钟读完《10 分钟学会 JavaScript 的 Async/Await》

那么,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() 可能会导致一些意料之外的问题。

Comments
Write a Comment