让Express支持async方法分享

本文主要介绍了详解如何让express支持async/await,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望能帮助到大家。

随着 Node.js v8 的发布,Node.js 已原生支持 async/await 函数,Web 框架 Koa 也随之发布了 Koa 2 正式版,支持 async/await 中间件,为处理异步回调带来了极大的方便。

既然 Koa 2 已经支持 async/await 中间件了,为什么不直接用 Koa,而还要去改造 Express 让其支持 async/await 中间件呢?因为 Koa 2 正式版发布才不久,而很多老项目用的都还是 Express,不可能将其推倒用 Koa 重写,这样成本太高,但又想用到新语法带来的便利,那就只能对 Express 进行改造了,而且这种改造必须是对业务无侵入的,不然会带来很多的麻烦。

直接使用 async/await

让我们先来看下在 Express 中直接使用 async/await 函数的情况。

const express = require('express');const app = express();const { promisify } = require('util');const { readFile } = require('fs');const readFileAsync = promisify(readFile);  app.get('/', async function (req, res, next){ const data = await readFileAsync('./package.json'); res.send(data.toString());});// Error Handlerapp.use(function (err, req, res, next){ console.error('Error:', err); res.status(500).send('Service Error');});  app.listen(3000, '127.0.0.1', function (){ console.log(`Server running at http://${this.address().address }:${this.address().port }/`);});

登录后复制

上面是没有对 Express 进行改造,直接使用 async/await 函数来处理请求,当请求 http://127.0.0.1:3000/ 时,发现请求能正常请求,响应也能正常响应。这样似乎不对 Express 做任何改造也能直接使用 async/await 函数,但如果 async/await 函数里发生了错误能不能被我们的错误处理中间件处理呢?现在我们去读取一个不存在文件,例如将之前读取的 package.json 换成 age.json 。

app.get('/', async function (req, res, next){ const data = await readFileAsync('./age.json'); res.send(data.toString());});

登录后复制

现在我们去请求 http://127.0.0.1:3000/ 时,发现请求迟迟不能响应,最终会超时。而在终端报了如下的错误:

让Express支持async方法分享

发现错误并没有被错误处理中间件处理,而是抛出了一个 unhandledRejection 异常,现在如果我们用 try/catch 来手动捕获错误会是什么情况呢?

app.get('/', async function (req, res, next){ try {  const data = await readFileAsync('./age.json');  res.send(datas.toString()); } catch(e) {  next(e); }});

登录后复制

发现请求被错误处理中间件处理了,说明我们手动显式的来捕获错误是可以的,但是如果在每个中间件或请求处理函数里面加一个 try/catch 也太不优雅了,对业务代码有一定的侵入性,代码也显得难看。所以通过直接使用 async/await 函数的实验,我们发现对 Express 改造的方向就是能够接收 async/await 函数里面抛出的错误,又对业务代码没有侵入性。

改造 Express

在 Express 中有两种方式来处理路由和中间件,一种是通过 Express 创建的 app,直接在 app 上添加中间件和处理路由,像下面这样:

const express = require('express');const app = express();  app.use(function (req, res, next){ next();});app.get('/', function (req, res, next){ res.send('hello, world');});app.post('/', function (req, res, next){ res.send('hello, world');});  app.listen(3000, '127.0.0.1', function (){ console.log(`Server running at http://${this.address().address }:${this.address().port }/`);});

登录后复制

另外一种是通过 Express 的 Router 创建的路由实例,直接在路由实例上添加中间件和处理路由,像下面这样:

const express = require('express');const app = express();const router = new express.Router();app.use(router);  router.get('/', function (req, res, next){ res.send('hello, world');});router.post('/', function (req, res, next){ res.send('hello, world');});  app.listen(3000, '127.0.0.1', function (){ console.log(`Server running at http://${this.address().address }:${this.address().port }/`);});

登录后复制

这两种方法可以混合起来用,现在我们思考一下怎样才能让一个形如 app.get(‘/’, async function(req, res, next){}) 的函数,让里面的 async 函数抛出的错误能被统一处理呢?要让错误被统一的处理当然要调用 next(err) 来让错误被传递到错误处理中间件,又由于 async 函数返回的是 Promise,所以肯定是形如这样的 asyncFn().then().catch(function(err){ next(err) }) ,所以按这样改造一下就有如下的代码:

app.get = function (...data){ const params = []; for (let item of data) {  if (Object.prototype.toString.call(item) !== '[object AsyncFunction]') {   params.push(item);   continue;  }  const handle = function (...data){   const [ req, res, next ] = data;   item(req, res, next).then(next).catch(next);  };  params.push(handle); } app.get(...params)}

登录后复制

上面的这段代码中,我们判断 app.get() 这个函数的参数中,若有 async 函数,就采用 item(req, res, next).then(next).catch(next); 来处理,这样就能捕获函数内抛出的错误,并传到错误处理中间件里面去。但是这段代码有一个明显的错误就是最后调用 app.get(),这样就递归了,破坏了 app.get 的功能,也根本处理不了请求,因此还需要继续改造。

我们之前说 Express 两种处理路由和中间件的方式可以混用,那么我们就混用这两种方式来避免递归,代码如下:

const express = require('express');const app = express();const router = new express.Router();app.use(router);  app.get = function (...data){ const params = []; for (let item of data) {  if (Object.prototype.toString.call(item) !== '[object AsyncFunction]') {   params.push(item);   continue;  }  const handle = function (...data){   const [ req, res, next ] = data;   item(req, res, next).then(next).catch(next);  };  params.push(handle); } router.get(...params)}

登录后复制

像上面这样改造之后似乎一切都能正常工作了,能正常处理请求了。但通过查看 Express 的源码,发现这样破坏了 app.get() 这个方法,因为 app.get() 不仅能用来处理路由,而且还能用来获取应用的配置,在 Express 中对应的源码如下:

methods.forEach(function(method){ app[method] = function(path){  if (method === 'get' && arguments.length === 1) {   // app.get(setting)   return this.set(path);  }    this.lazyrouter();    var route = this._router.route(path);  route[method].apply(route, slice.call(arguments, 1));  return this; };});

登录后复制

所以在改造时,我们也需要对 app.get 做特殊处理。在实际的应用中我们不仅有 get 请求,还有 post、put 和 delete 等请求,所以我们最终改造的代码如下:

const { promisify } = require('util');const { readFile } = require('fs');const readFileAsync = promisify(readFile);const express = require('express');const app = express();const router = new express.Router();const methods = [ 'get', 'post', 'put', 'delete' ];app.use(router);  for (let method of methods) { app[method] = function (...data){  if (method === 'get' && data.length === 1) return app.set(data[0]);  const params = [];  for (let item of data) {   if (Object.prototype.toString.call(item) !== '[object AsyncFunction]') {    params.push(item);    continue;   }   const handle = function (...data){    const [ req, res, next ] = data;    item(req, res, next).then(next).catch(next);   };   params.push(handle);  }  router[method](...params); };}   app.get('/', async function (req, res, next){ const data = await readFileAsync('./package.json'); res.send(data.toString());});   app.post('/', async function (req, res, next){ const data = await readFileAsync('./age.json'); res.send(data.toString());});  router.use(function (err, req, res, next){ console.error('Error:', err); res.status(500).send('Service Error');});    app.listen(3000, '127.0.0.1', function (){ console.log(`Server running at http://${this.address().address }:${this.address().port }/`);});

登录后复制

现在就改造完了,我们只需要加一小段代码,就可以直接用 async function 作为 handler 处理请求,对业务也毫无侵入性,抛出的错误也能传递到错误处理中间件。

相关推荐:

NodeJs通过async和await处理异步的方法

Node.js中如何使用async函数

ES6之async+await同步/异步方案详解

以上就是让Express支持async方法分享的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2788744.html

(0)
上一篇 2025年3月8日 18:30:44
下一篇 2025年3月5日 20:56:52

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • ES6模板字符串实例分享

    es6 中引进的一种新型的字符串字面量语法 – 模板字符串。书面上来解释,模板字符串是一种能在字符串文本中内嵌表示式的字符串字面量。简单来讲,就是增加了变量功能的字符串。 ES6为我们提供了模板字符串,语法使用反引号`。模板字符…

    编程技术 2025年3月8日
    200
  • express与koa的使用对比

    express和koa都是服务端的开发框架,服务端开发的重点是对http request和http response两个对象的封装和处理,应用的生命周期维护以及视图的处理等。 提到Node.js开发,不得不提目前炙手可热的2大框架expre…

    2025年3月8日
    200
  • 几种npm依赖包管理分享

    本文主要给大家介绍了关于你应该知道的几类npm依赖包管理,npm 是node.js 里的包管理器,是一个命令行工具,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。 npm 目前支持以下几类依赖包管理:…

    编程技术 2025年3月8日
    200
  • Vue只弹一次的弹框实例分享

    本文主要和大家分享vue只弹一次的弹框的实例,希望能帮助大家更好的使用vue开发。 核心代码是 getCookie()部分,控制弹框的显示隐藏则在 created()中。 Lorem ipsum dolor sit amet, consec…

    编程技术 2025年3月8日
    200
  • js使用可遍历数组的API实例分享

    js中有很多可以遍历数组的api,既然已经封装的这么好,为什么不在平常开发的时候使用,本文讲讲foreach、map、filter、some、every、reduce这些api的使用,并且和普通的for语句作对比。本文主要和大家分享js使用…

    编程技术 2025年3月8日
    200
  • VUE后台管理界面案例分享

    本文我们主要和大家分享VUE后台管理界面案例,主要功能有以下。 登录退出功能 国际化中英文界面切换 动态菜单列表 通过动态页签增减实现组件切换展示 路由切换菜单功能 立即学习“前端免费学习笔记(深入)”; vue 实现网页版前端框架搭建,只…

    2025年3月8日
    200
  • node通过实现express搭建自己的服务器实例

    本文主要介绍node通过express搭建自己的服务器 ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望能帮助到大家。 前言 为了模拟项目上线,我们就需要一个服务器去提供API给我们调用数据。这次我采用ex…

    编程技术 2025年3月8日
    200
  • Vue from-validate表单验证代码分享

    本文主要和大家介绍vue from-validate 表单验证的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望能帮助到大家。 前言 需要进行表单数据验证,原先才用html5来完成验证,但是效果很差…

    编程技术 2025年3月8日
    200
  • 全面掌握Express cookie-parser中间件

    cookie-parser 是express的中间件,用来实现cookie的解析,是官方脚手架内置的中间件之一。它的使用非常简单,但在使用过程中偶尔也会遇到问题。一般都是因为对 express + cookie-parser 的签名、验证机…

    编程技术 2025年3月8日
    200
  • Vue组件化开发经验分享

    本文主要和大家分享Vue组件化开发经验分享,希望这些vue组件化开发的思想能帮助到大家。 既有认知(误) 一般说到组件,我首先想到的是弹窗,其他就大脑空白了。因为觉得这个是在项目中最常用的功能,提取出来方便复用的才是组件~ 然而我才发现这个…

    2025年3月8日
    200

发表回复

登录后才能评论