文档结构  
翻译进度:已翻译     翻译赏金:10 元 (?)    ¥ 我要打赏

ES7 的新特性 Async/await 允许我们这些开发者像写同步代码一样写异步代码。现在,我们可以在JS中使用Promises对象,他可以简化异步流,避免回调地域。

回调地域一词描述的是下面这种状况:

Js

function AsyncTask() {  
   asyncFuncA(function(err, resultA){
      if(err) return cb(err);

      asyncFuncB(function(err, resultB){
         if(err) return cb(err);

          asyncFuncC(function(err, resultC){
               if(err) return cb(err);

               // And so it goes....
          });
      });
   });
}
第 1 段(可获 0.63 积分)

这样的代码不仅难以维护,还让控制流变得异常艰巨。考虑这样一个 if 语句,如果需要在 callbackA 的返回值为 'foo' 的时候执行其它异步方法。

Promise 的拯救

随着  ES6 及 Promise 的发布,我们像这样可以简化前面那段悲剧的代码:

Js

function asyncTask(cb) {

   asyncFuncA.then(AsyncFuncB)
      .then(AsyncFuncC)
      .then(AsyncFuncD)
      .then(data => cb(null, data)
      .catch(err => cb(err));
}

你不觉得这段代码要好看些吗?

不过真实的开发中异步流可能会更复杂一些,比如服务端的模型(nodejs)中,你想在数据库中保存一个实体,然后基于保存的值查找其它实体,如果值存在则进行另外的异步任务,在所有任务完成之后需要向用户返回第1步创建的对象。如果任意步骤发生错误,需要准确地向用户发出错误通知。

第 2 段(可获 1.91 积分)

用上了promises以后,理所当然代码比平常callback的方式干净一点。但是,恕我直言,还是有一点乱。

ES7 Async/await

注意,为享受async/await语法,你需要一个转译器。由于需要polyfill,所以你可以使用babel或者typescript。

这是我发现async/await语法真正有用的地方:你可以把代码写成下面这样。

Js

async function asyncTask(cb) {  
    const user = await UserModel.findById(1);
    if(!user) return cb('No user found');

    const savedTask = await TaskModel({userId: user.id, name: 'Demo Task'});

    if(user.notificationsEnabled) {
         await NotificationService.sendNotification(user.id, 'Task Created');  
    }

    if(savedTask.assignedUser.id !== user.id) {
        await NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you');
    }

    cb(null, savedTask);
}
第 3 段(可获 0.84 积分)

上面的代码看起来真简洁,不过,怎么进行错误处理?

在执行 Promise 期间的异步调用有可能发生错误(DB 连接错误、数据模型错误,等)。

由于异步函数在等待 Promise,所以不当 Promise 遇到错误的时候会抛出异常,并可由 Promise 的 catch 方法捕捉到。

在 async/await 方式中一般使用 try/catch 块来捕捉这类错误。

我不懂强类型语言,所以我觉得 try/catch 给我添加了额外的代码,看起来不那么舒服。我相信这与个人喜好有关,但在我看来就是这样。

第 4 段(可获 1.46 积分)

所以前端的代码会改成像这样:

Js

async function asyncTask(cb) {  
    try {
       const user = await UserModel.findById(1);
       if(!user) return cb('No user found');
    } catch(e) {
        cb('Unexpected error occurred');
    }

    try {
       const savedTask = await TaskModel({userId: user.id, name: 'Demo Task'});
    } catch(e) {
        cb('Error occurred while saving task');
    }

    if(user.notificationsEnabled) {
        try {
            await NotificationService.sendNotification(user.id, 'Task Created');  
        } catch(e) {
            cb('Error while sending notification');
        }
    }

    if(savedTask.assignedUser.id !== user.id) {
        try {
            await NotificationService.sendNotification(savedTask.assignedUser.id, 'Task was created for you');
        } catch(e) {
            cb('Error while sending notification');
        }
    }

    cb(null, savedTask);
}
第 5 段(可获 0.13 积分)

另辟蹊径

最近我在用go语言 ,我非常喜欢他们的解决方式,就像下面这样:

Go

data, err := db.Query("SELECT ...")  
if err != nil { return err }  

这种方式比try-catch块清楚,代码也少,所以容易读,也好维护。

问题在于 await ,如果不用try-catch,它会不声不响退出函数。如果没有catch语句,就无法控制它。

我和我的好友Tomer Barnea试着去寻找简洁的解决办法,最后找到的方法如下:

第 6 段(可获 1.31 积分)

还记得await会等待promise的解决吗?

利用这一点我们可以写一个小小的工具函数来帮助我们处理这些异常。

Js

// to.js
export default function to(promise) {  
   return promise.then(data => {
      return [null, data];
   })
   .catch(err => [err]);
}

这个工具函数接收promise作为参数,将成功的运行结果作为数组的第二项,而把catch中接收的异常作为数组的第一项。

这样我们的async代码就会变成这样:

Js

import to from './to.js';

async function asyncTask(cb) {  
     let err, user, savedTask;

     [err, user] = await to(UserModel.findById(1));
     if(!user) return cb('No user found');

     [err, savedTask] = await to(TaskModel({userId: user.id, name: 'Demo Task'}));
     if(err) return cb('Error occurred while saving task');

    if(user.notificationsEnabled) {
       const [err] = await to(NotificationService.sendNotification(user.id, 'Task Created'));  
       if(err) return cb('Error while sending notification');
    }

    cb(null, savedTask);
}

上面的例子只是解决方案的一个简单用例,您可以在to.js方法中附加拦截器,该方法将接收原始错误对象,记录或执行任何您需要执行的操作,然后再传回。

我们为此库创建了一个简单的NPM包,您可以使用以下方式安装它:Github Repo

Bash

npm i await-to-js  

这篇文章只是一个查看async /await功能的不同方式,它是完全基于个人观点。 您可以使用promises,单个try-catch和许多其他解决方案来实现类似的结果。 只要你喜欢的东西,它适用于你坚持下去:)

第 8 段(可获 1.36 积分)

文章评论

边城
翻译了两段,受到启发,写了篇 [从不用 try-catch 实现的 async/await 语法说错误处理](https://segmentfault.com/a/1190000011802045)