Node.js Async 函数最佳实践

析构涟漪
• 阅读 3711

Node.js Async 函数最佳实践

Node.js7.6起, Node.js 搭载了有async函数功能的V8引擎。当Node.js 8于10月31日成为LTS版本后,我们没有理由不使用async函数。接下来,我将简要介绍async函数,以及如何改变我们编写Node.js应用程序的方式。

什么是async函数

async函数可以让你编写基于Promise的代码使他像同步的一样。每当你用asnyc关键字定义了一个函数体,你可以在函数体内使用 await关键字。当这个async函数被调用,会得到一个Promise实例。当async函数返回值时这个promise会执行。如果async函数抛出错误,那么会进入promise的rejected流程。

await关键字可以用来等待Promise 进入 resolved并有完成返回值。如果传给await的值不是Promise实例,它会被转为 Promiseresolved 流程。

    const rp = require('request-promise');
    
    async function main () {
        const result = await rp('https://google.com');
        const twenty = await 20;
    
        // sleeeeeeeeping  for a second
        await new Promise (resolve => {
        setTimeout(resolve, 1000);
        });
        
        return result
    }

    main()
        .then(console.log)
        .catch(console.error);

迁移到async函数

如果你的Node.js应用已经使用了Promise, 你只需要使用 await替代Promise链式调用。如果你的代码是基于 callback, 迁移到 async函数需要逐步修改现有代码。可以在新到功能中使用新的技术,如果必须保留旧有功能则可以使用 Promise 进行简单的包装。为此你可以使用内置的util.promisify(译者注:Node.js 8.0+) 方法!

    const util = require('util');
    const {readFile} = require('fs');
    const readFileAsync = util.promisify(readFile);
    
    async function main () {
      const result = await readFileAsync('.gitignore');
    
      return result
    }
    
    main()
      .then(console.log)
      .catch(console.error);

async函数最佳实践

express中使用async函数

As express supports Promises out of the box, using async functions with express is as simple as:

express 是支持 Promise的,所以使用async函数可以把代码简化为:

    const express = require('express');
    const app = express();
    
    app.get('/', async (request, response) => {
        // awaiting Promises here
        // if you just await a single promise, you could simply return with it,
        // no need to await for it
        const result = await getContent();
        
        response.send(result);
    });
    
    app.listen(process.env.PORT);

Edit1:如Keith Smith所指出的那样,上面的例子有一个严重的问题 - 如果Promise进入rejected,express路由处理程序就会hang住,因为那里没有错误处理。

要解决这个问题,你应该把你的异步处理程序封装在一个处理错误的函数中:

    const awaitHandlerFactory = middleware => {
    
        return async (req, res, next) => {
            try {
              await middleware(req, res, next)
            } catch (err) {
              next(err)
            }
        }
    }
    
    // and use it this way:
    app.get('/', awaitHandlerFactory(async (request, response) => {
        const result = await getContent();
        
        response.send(result);
    }));

并行

假设你正在做类似的事情,当一个操作需要两个输入,一个来自数据库,另一个来自外部服务:

    async function main () {
      const user = await Users.fetch(userId);
      const product = await Products.fetch(productId);
    
      await makePurchase(user, product);
    }

在这个case中,将会发生以下情况:

  • 你的代码将首先获得用户资源,
  • 然后获得产品资源,
  • 并最终进行购买。

正如所见,你可以同时做前两个操作,因为它们之间没有依赖关系。 为此应该使用 Promise.all 方法:

    async function main () {
        const [user, product] = await Promise.all([
            Users.fetch(userId),
            Products.fetch(productId)
        ]);
        
        await makePurchase(user, product);
    }

在某些情况下,您只需要最快resolving得到Promise的结果 - 在这种情况时可以使用Promise.race方法。

错误处理

参考下面的代码

    async function main () {
        await new Promise((resolve, reject) => {
            reject(new Error('💥'));
        });
    };
    
    main()
        .then(console.log);

如果运行这段代码,你会在terminal上看到类似的消息:

    (node:69738) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: 💥
    (node:69738) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

在新版的Node.js中,如果Promise拒绝不会被处理,那么会导致整个Node.js进程崩溃。 因此在必要时应该使用try-catch语句:

    const util = require('util');
    
    async function main () {
        try {
            await new Promise((resolve, reject) => {
              reject(new Error(' '));
            });
        } 
        catch (err) {
        // handle error case
        // maybe throwing is okay depending on your use-case
        }
    }
    
    main()
        .then(console.log)
        .catch(console.error);

但是如果使用try-catch会丢失重要的异常如系统错误,那么就要重新抛出异常。 要了解更多关于什么时候应该重新投掷的信息,我强烈建议阅读Eran的Learning to Throw Again.

复杂的控制流程

Node.js的第一个异步控制流库是由 Caolan McMahon 编写的一async的异步控制流库。 它提供了多个异步助手:

  • mapLimit,
  • filterLimit,
  • concatLimit,
  • priorityQueue.

如果你不想重新造轮子,而且不想使用这个库,那么可以重度使用 async函数和 util .promisify方法:

    const util = require('util');
    const async = require('async');
    const numbers = [
        1, 2, 3, 4, 5
    ];

    mapLimitAsync = util.promisify(async.mapLimit);
    
    async function main () {
        return await mapLimitAsync(numbers, 2, (number, done) => {
            setTimeout(function () {
                done(null, number * 2);
            }, 100)
        });
    };
    
    main()
        .then(console.log)
        .catch(console.error);

原文地址


点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Easter79 Easter79
3年前
sql:mysql:函数:TIMESTAMPDIFF函数实现TimeStamp字段相减,求得时间差
<divclass"htmledit\_views"id"content\_views"<p&nbsp;函数内指定是minute,则最终结果value值的单位是分钟,如果函数内指定为hours,则最终结果value值单位为小时。</p<preclass"has"name"code"<codeclass"hljssql"<
Wesley13 Wesley13
3年前
SQL利用函数或存储过程求男或女的总分平均分
!(https://oscimg.oschina.net/oscnet/633e11621f3e13e713cf063db00d72c8aa0.png)函数alterfunctionxb(@xingbievarchar(2))returnstableas
Stella981 Stella981
3年前
Node.js
1.Node来历   2009年,正是推出基于Javascript语言和V8引擎的开源Web服务项目,命名为Node.js,Node.js是第一次把Javascript带到后端开发。全很很多开发人员都熟悉Javascript,所以Node.js一下子就火了。   Javascript语言本身是完善的函数式语言,在前端开发时,开发
Stella981 Stella981
3年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
3年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
3年前
PHP中的NOW()函数
是否有一个PHP函数以与MySQL函数NOW()相同的格式返回日期和时间?我知道如何使用date()做到这一点,但是我问是否有一个仅用于此的函数。例如,返回:2009120100:00:001楼使用此功能:functiongetDatetimeNow(){
当我们在谈论构造函数注入的时候我们在谈论什么 | 京东物流技术团队
依赖注入当涉及依赖注入(DependencyInjection,DI)时,首先推荐使用构造函数注入,因为构造函数注入有很多技术优点,而且还与面向对象的设计原则密切相关。在业界,构造函数注入作为依赖注入的一种最佳实践得到了广泛的认可,在SpringFrame