发布于 2026-01-06 3 阅读
0

在 Express 中编写 Async/wait 中间件

在 Express 中编写 Async/wait 中间件

你的Express应用是否使用基于 Promise 的数据获取?你是否厌倦了在.then()方法调用中嵌套路由处理代码语句?你是否想以一种实用且易于应用的方式学习async/await?如果你对以上任何一个问题的回答是“是”,那么请继续阅读,我将帮助你把这些 Promise 移到异步中间件函数中。

(如果您只想查看包含完整代码的示例仓库,请点击这里查看

问题

如果你和我一样,你的 Express 路由器会使用基于回调的方式,通过向相应的 API 端点或数据库请求数据来获取数据。这是一种现代方法,旨在缓解许多开发者所说的“回调地狱Promise”问题

以下代码片段是根据我目前的一个 Express 项目建模的;请注意,它使用异步的、基于 Promise 的数据传递结构,而不是同步的、基于函数的回调。

/* Example of a Promise-based data request approach */
const endpoint = 'https://jsonplaceholder.typicode.com/posts/1';
app.get('/',(req,res) => {
  PromiseBasedDataRequest(endpoint).then(data => {
    const { title, body } = data;
    req.render('post', { title, body });
  })
})
Enter fullscreen mode Exit fullscreen mode

所以我们现在使用的是相对扁平的 Promise,而不是层层嵌套的回调函数金字塔,太好了!

但是,一旦数据通过调用链传递完毕.then(),我们就需要编写大量代码来处理数据和/或发送路由器请求的实际响应。有些开发者会以这种方式编写代码,如果这种方式对你来说行得通,那当然很好;然而,既然 Node 支持 async/await,就不必再这样做了。

我的解决方法

从 Node.js v7.6 开始,它支持异步函数(通常称为异步函数),现在我们可以直接从中间件函数中解析的异步函数中提取数据,并以清晰易读的方式将该数据传递给最终的路由器回调。async/awaitPromiseasync

请将以下中间件函数视为对前面代码片段的更新:

const endpoint = 'https://jsonplaceholder.typicode.com/posts/1';
const asyncMiddleware = async (req,res,next) => {
  const data = await PromiseBasedDataRequest(endpoint);
  req.data = data.json()
  next()
}
Enter fullscreen mode Exit fullscreen mode

您可能已经注意到我们添加了一些内容:

  1. 函数声明前的关键字async;这向引擎表明,在函数体的某个地方,有一个对 Promise 的调用,并且通常 Promise 会使用关键字await(参见 #2)。
  2. await调用语句前的关键字PromiseBasedDataRequest赋值给data变量;这告诉引擎所有其他代码的执行都应该暂停,直到 Promise 解析完成。(即应用程序“等待”请求的结果)这也允许PromiseValue将 Promise 赋值给一个变量并在以后使用。

之后,从 /request 对象data获取的值endpoint会被赋给一个属性req;这样,应用程序稍后就可以通过该属性访问该值req.data。最后,调用 `get()` 方法会将` and`对象next()发送到下一个中​​间件,或者,如果没有其他中间件,则会发送到最终的路由回调。reqres

让我们把asyncMiddleware函数放到路由链中;在 Express 中,这发生在路由和最终回调之间。(你可以在这里放置任意数量的中间件函数,但不要忘记next()在每个函数结束时调用它!)

app.get('/', asyncMiddleware, (req,res) => {
  const { title, body } = req.data;
  req.render('post', { title, body });
})
Enter fullscreen mode Exit fullscreen mode

哇!好清新,好干净。

现在我们有了一个非常扁平且易读的路由器声明,我们大部分原本应该放在路由器回调函数中的代码[1]现在都放在了一个中间件函数中,数据同步地传递到最终的路由器回调函数中。

如果您想在一个“已完成”的项目中查看此实现/模式,请查看我为本文创建的示例仓库。请按照 README 文件中的说明进行操作;如果您发现任何问题或需要帮助,请随时提交 issue,我将很乐意与您一起查看。

动机

我之所以研究这个解决方案,是因为我想更多地学习和尝试async/await函数和 Express 中间件;我学习的最好方法就是制作测试项目并获得实现功能的实际经验。

我计划在一个使用 JavaScript Contentful SDKMySQL 驱动程序的副项目中采用这种编写异步中间件的模式。目前,我在这个项目中的实现与第一个代码片段示例完全相同:调用相应的 API,并将实际的 ` res/response` 调用包装在回调函数或.then()方法调用中。通过将这些功能重写为中间件函数,我旨在简化编写新的路由路径/API 端点,并希望提高代码的复用性。

结论

总之,使用async/await作为 Express 中间件实现有助于保持代码的可重用性、可读性,并符合当前的编码规范。

希望我周六做的这个小实验能帮助你更好地理解异步函数和 Express 中间件。感谢阅读!

延伸阅读

笔记

[1]:根据这篇 Stack Overflow 帖子,未解决的 Promise 不会造成严重的内存泄漏威胁。

文章来源:https://dev.to/geoff/writing-asyncawait-middleware-in-express-6i0