将异步函数传递给Node.js Express.js路由器

40

这似乎是一个简单的谷歌搜索,但我似乎找不到答案...

你能否将ES7异步函数传递给Express路由器?

例如:

var express = require('express');
var app = express();

app.get('/', async function(req, res){
  // some await stuff
  res.send('hello world');
});

如果没有,你能指导我如何用ES7的方式解决这个问题吗?还是说我只能使用Promises?

谢谢!


我认为Express已经以异步方式处理请求。当其他请求进来时,它不会等待直到此请求完成。 - James Maa
1
我有一种预感,异步函数应该可以工作...但我很想看到文档来证明我的想法是对还是错。 - ForgetfulFellow
2
https://www.npmjs.com/package/express-async-router 我认为这是你在寻找的东西? - James Maa
2
Express路由只接受回调函数。Node支持异步函数。因此,如果路由的回调函数恰好是异步的,Node将视其为异步函数。顺便说一下,它可以正常工作,我已经做过了。 - pizzarob
7个回答

37

可能您没有找到结果,因为async/await是ES7而不是ES6功能,它在node版本>= 7.6中可用。

您的代码将在node中工作。我已测试了以下代码。

var express = require('express');
var app = express();

async function wait (ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms)
  });
}

app.get('/', async function(req, res){
  console.log('before wait', new Date());
  await wait(5 * 1000);
  console.log('after wait', new Date())
  res.send('hello world');
});

app.listen(3000, err => console.log(err ? "Error listening" : "Listening"))

就这样,完成了!

MacJamal:messialltimegoals dev$ node test.js 
Listening undefined
before wait 2017-06-28T22:32:34.829Z
after wait 2017-06-28T22:32:39.852Z
^C

基本上你已经明白了,你需要将一个函数异步化,以便在函数内部await一个promise。但是在node LTS v6中不支持这种功能,所以可以使用Babel来转译代码。希望这可以帮到你。


17
警告:如果您直接使用异步函数,Express 将停止处理错误并且不会响应 500 状态码。实际上,如果发生错误,Express 将根本不会响应,客户端将一直等待直到超时。 - Madacol
5
从文档中可以看出,自Express 5开始,返回Promise的路由处理程序和中间件在拒绝或抛出错误时将自动调用next(value)。 - laxman
任何不考虑特殊情况的答案都不是一个真正的答案。 - Michael Bushe
2
@MichaelBushe 一切皆为答案,它可以帮助你理解问题。 - milos

14

更新

自从ExpressJs 5以来,异步函数被支持并可以像预期的那样抛出错误。

从 Express 5 开始,返回Promise的路由处理程序和中间件将在拒绝或抛出错误时自动调用 next(value)。
来源


在Express 4或更低版本中,它有点起作用,但实际上并没有

虽然似乎有效,但它停止了处理在异步函数内部抛出的错误,因此,如果未处理错误,则服务器永远不会响应,并且客户端将保持等待状态,直到超时为止。

正确的行为应该是响应500状态码。


解决方案

express-promise-router

const router = require('express-promise-router')();

// Use it like a normal router, it will handle async functions

express-asyncify


这是一个与Node.js框架Express一起使用的模块,它可以轻松地将原来使用回调函数的异步代码转换为使用async/await语法的代码。
const asyncify = require('express-asyncify')

解决在app对象中设置路由的问题

使用以下代码替换var app = express();

var app = asyncify(express());

修复设置在router对象中的路由

用以下代码替换var router = express.Router();

var router = asyncify(express.Router());

注意

只需要在直接设置路由的对象中应用asyncify函数。

https://www.npmjs.com/package/express-asyncify


1
请注意,Express 5 将正确处理异步错误:https://dev59.com/NVcP5IYBdhLWcg3wJnDp#67689269 - Ciro Santilli OurBigBook.com
Express 5已经好几年没有发布了,也有可能永远不会发布。 - moltenform

4

这与所问的问题有什么关系?异常并不是问题的重点,尽管如果您解释了有关传递异步函数的内容,那就太好了。 - Arpit Solanki
3
这是一个非常重要的副作用,它会使得男性表达出现错误而不显示它们,并且请求将永远不会返回,从而导致客户端超时。 - Zalaboza

2

Express 5将自动处理async错误

https://expressjs.com/en/guide/error-handling.html目前已经明确说明:

Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error. For example:

app.get('/user/:id', async function (req, res, next) {
 var user = await getUserById(req.params.id)
 res.send(user)
})

If getUserById throws an error or rejects, next will be called with either the thrown error or the rejected value. If no rejected value is provided, next will be called with a default Error object provided by the Express router.

我们可以按照以下方式进行测试:
const assert = require('assert')
const http = require('http')

const express = require('express')

const app = express()
app.get('/error', async (req, res) => {
  throw 'my error'
})

const server = app.listen(3000, () => {
  // Test it.
  function test(path, method, status, body) {
    const options = {
      hostname: 'localhost',
      port: server.address().port,
      path: path,
      method: method,
    }
    http.request(options, res => {
      console.error(res.statusCode);
      assert(res.statusCode === status);
    }).end()
  }
  test('/error', 'GET', 500)
})

express@5.0.0-alpha.8 版本的终端输出是预期的:

500
Error: my error
    at /home/ciro/test/express5/main.js:10:9
    at Layer.handle [as handle_request] (/home/ciro/test/node_modules/router/lib/layer.js:102:15)
    at next (/home/ciro/test/node_modules/router/lib/route.js:144:13)
    at Route.dispatch (/home/ciro/test/node_modules/router/lib/route.js:109:3)
    at handle (/home/ciro/test/node_modules/router/index.js:515:11)
    at Layer.handle [as handle_request] (/home/ciro/test/node_modules/router/lib/layer.js:102:15)
    at /home/ciro/test/node_modules/router/index.js:291:22
    at Function.process_params (/home/ciro/test/node_modules/router/index.js:349:12)
    at next (/home/ciro/test/node_modules/router/index.js:285:10)
    at Function.handle (/home/ciro/test/node_modules/router/index.js:184:3)

如果您在浏览器上访问它,您将看到一个HTML页面,上面写着“我的错误”。
如果您在express@4.17.1上运行完全相同的代码,则仅在终端上看到:
UnhandledPromiseRejectionWarning: my error

但不是 500 或者 my error。这是因为请求永远停留在那里。如果你尝试在浏览器中打开它,你会更清楚地看到它的停滞。

待办事项:如何使其显示堆栈跟踪,而不仅仅是 my errorGetting the stack trace in a custom error handler in Express?

Express 4 解决方案

对于 Express 4 的最简单解决方案就是在每个路由中加入一个 try/catch,如下所示:

app.get('/error', async (req, res, next) => {
  try {
    throw new Error('my error'
    res.send('never returned')
  } catch(error) {
    next(error);
  }
})

这将产生与Express 5相同的正确行为。

您还可以通过一些在express.js异步路由和错误处理中讨论的方法进一步优化此过程。

已在Node.js v14.16.0上进行了测试。


1
Express 5已经多年没有发布了,而且可能永远不会发布。 - moltenform
@moltenform 有些人仍然相信:https://github.com/expressjs/express/issues/4920#issuecomment-1575635801 - Ciro Santilli OurBigBook.com

1
使用 express-promise-router
const express = require('express');
const Router = require('express-promise-router');
const router = new Router();   
const mysql = require('mysql2');

const pool = mysql.createPool({
  host: 'localhost',
  user: 'myusername',
  password: 'mypassword',
  database: 'mydb',
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
}).promise();

router.get('/some_path', async function(req, res, next) {
  const [rows, ] = await pool.execute(
    'SELECT * ' +
    'FROM mytable ',
    []
  );

  res.json(rows);
});

module.exports = router;

使用mysql2的promise接口与express-promise-router一起使用的示例如上所示。

0
为了处理Express路由中的异步请求,请使用try-catch,它可以帮助您尝试函数中的任何错误并捕获它们。 try{await stuff} catch{err}

0
另一个不那么侵入式的选项是使用 express-async-errors
这将修补express以正确处理async/await。
import express from 'express'
import 'express-async-errors'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接