Nodejs-Async-函数最佳实践

44次阅读

共计 3681 个字符,预计需要花费 10 分钟才能阅读完成。

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);

原文地址


  • 我的 blog: neverland.github.io
  • 我的 email enix@foxmail.com

正文完
 0