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


Posted in NodeJs onOctober 09, 2017

场景

远古时代

我们在编写express后台,经常要有许多异步IO的处理。在远古时代,我们都是用chunk函数处理,也就是我们最熟悉的那种默认第一个参数是error的函数。我们来模拟一个Mongo数据库的操作,感受一下。

mongoDb.open(function(err, db){
  if(!err){
    db.collection("users", function(err, collection){
      if(!err){
        let person = {name: "yika", age: 20};
        collection.insert(person, function(err, result){
          if(!err){
            console.log(result);
          }
        });
      }
    })
  }
});

这个也就是被我们所诟病的callback hell,一堆横向金字塔,如果将回调拆分成函数,则会变得非常支离破碎。为了防止到恶心到大家,我甚至没有写关于错误的处理,正常来说,每一个异步的操作都需要都它的error进行相应的显示或处理的。

Promise时代

后来进入了好一点的时代就是Promise,我们也可以称作链式操作。关于Promise,我也是之前有专门写过一系列的博文,有兴趣可以回头翻一下。这里来看看,将以上改写之后的状况。

let person = {name: "yika"};
mongoDb
  .open()
  .then(function(database){
   return database.collection("users");
  })
  .then(function(collection){
   return collection.insert(person);
  })
  .then(function(result){
   console.log(result);
  })
  .catch(function(e){
   throw new Error(e);
  })

我们可以看到,我们将金字塔已经平铺成一条线状结构了。相比之前恶心难以维护的chunk函数,变成了promise函数,并且错误的处理也变得十分优雅。但是我们仍然不可忽视某些问题,例如我们必须忍受各个逻辑被一个又一个的then()包裹起来,每一个函数都有其独立的作用域,如果为了共享某个数据就必须挂在最外层,最重要的还是,它与我们熟悉的同步编程仍然有差别。

Generator时代

TJ大神,借着ES6的Generator迭代器,最早实现了异步编程同步化的功能,也就是最为我们所熟知的co库。我们通过co(function *(){})可以使函数内部通过迭代器来控制。而co在这里则是充当了启动器的角色。关于Generator和co我在之前的博文也同样说过。

let co = require("co");

co(function *(){
  let db, collection, result; 
  let person = {name: "yika"};
  try{
    db = yield mongoDb.open();
    collection = yield db.collection("users");
    result = yield collection.insert(person);
  }catch(e){
    console.error(e.message);
  }
  console.log(result);
});

我们已经非常接近同步编程了,在co包裹的函数内部,只有一个异步执行完毕,才会继续执行下面的代码。并且错误的处理也是通过try and catch进行实现的。不过我们不得不承认的是,迭代器终究不是为异步而存在的。里面的yield*的语义也并不代表的就是异步函数标志。并且迭代器是需要co去驱动的,它和我们想象中的函数多少有一点点不同。

async/await时代

我们关注到ES7的async/await,才发现这才是我们想要的!我们将上面的代码小小改写一下。

async function insertData(person){
  let db, collection, result; 
  try{
    db = await mongoDb.open();
    collection = await db.collection("users");
    result = await collection.insert(person);
  }catch(e){
    console.error(e.message);
  }
  console.log(result);
} 

insertData({name: "yika"});

我们可以看到inserData是一个真正的函数,是我们可以直接去调用而无需启动器驱动的。当然内部我们也可以感受到处理yield变成了await以外,并没有很大区别。async/await,更符合我们异步编程的语义。

那么问题来了,how to use it?

使用

我们一开始就说过,babel已经支持async的transform了,所以我们使用的时候引入babel就行。当然server端和browser端,可以有不同的处理方法。在开始之前我们需要引入以下的package,preset-stage-3里就有我们需要的async/await的编译文件。

$ npm install babel-core --save
$ npm install babel-preset-es2015 --save
$ npm install babel-preset-stage-3 --save

Browser端

Babel一开始的出现就是为了让旧浏览器也能支持新的ES6特性,提升我们的开发体验。所以在Babel一开始就是可以通过babel-cli终端进行编译的。或者引入babel文件在浏览器端进行编译。当然这些都不是我最推荐的,所以我就带过不说啦。在前端静态资源配置里,webpack是现在比较好的解决方案,它支持静态资源的模块依赖,打包合并,还有语言的预处理,当然在这里我们就是指babel的处理。

// webpack.config.js
// 省略上面的文件输入输出的配置,直接看模块加载器的配置
module: {
  loaders: [
    {
      test: /\.js$/,
      exclude: /(node_modules|bower_components)/,
      loader: "babel",
      query: {
       presets: ['es2015', 'stage-3']
      }
    },
  ]
}

这样我们就可以愉快的使用了。

Server端

相对来说,后端比前端需要处理的异步IO地方多得多,也是更加需要这个。那我们在Server端又如何引入babel呢?

其实最简单也是最麻烦的方法就是,直接把js文件通过babel编译出新的文件再来使用。当然也就免不了冗余文件了,眼不见心不烦,还是换一个方法吧。

我们使用官方提供的require hook方法,顾名思义就是通过require进来后,接下来的文件进行require的时候都会经过Babel的处理。因为我们知道CommonJs是同步的模块依赖,所以也是可行的方法。我们需要多一个用于启动的js文件,一个真正执行程序的js文件。

// index.js 
// 用于引入babel,并且启动app.js

require("babel-core/register");
require("./app.js");

配置完hook之后,我们就配置babel的.babelrc文件,它是一个json格式的文件。es2015看情况配置,如果是已经是Node5.0版本,就无需再进行编译。

{
 "presets": ["stage-3", "es2015"]
}

最后我们的异步函数代码,写在app.js里即可。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

NodeJs 相关文章推荐
Nodejs极简入门教程(三):进程
Oct 27 NodeJs
nodejs实现获取当前url地址及url各种参数值
Jun 25 NodeJs
nodejs搭建本地服务器并访问文件的方法
Mar 03 NodeJs
nodejs入门教程一:概念与用法简介
Apr 24 NodeJs
nodejs后台集成ueditor富文本编辑器的实例
Jul 11 NodeJs
Mac 安装 nodejs方法(图文详细步骤)
Oct 30 NodeJs
nodejs 最新版安装npm 的使用详解
Jan 18 NodeJs
nodejs实现范围请求的实现代码
Oct 12 NodeJs
详解NodeJS Https HSM双向认证实现
Mar 12 NodeJs
Nodejs中的require函数的具体使用方法
Apr 02 NodeJs
详解NodeJs项目 CentOs linux服务器线上部署
Sep 16 NodeJs
通过实例了解Nodejs模块系统及require机制
Jul 16 NodeJs
nodejs 图片预览和上传的示例代码
Sep 30 #NodeJs
Nodejs调用WebService的示例代码
Sep 29 #NodeJs
Nodejs+angularjs结合multiparty实现多图片上传的示例代码
Sep 29 #NodeJs
Nodejs实现文件上传的示例代码
Sep 26 #NodeJs
详解nodejs通过代理(proxy)发送http请求(request)
Sep 22 #NodeJs
使用vs code开发Nodejs程序的使用方法
Sep 21 #NodeJs
详解使用vscode+es6写nodejs服务端调试配置
Sep 21 #NodeJs
You might like
php中目录,文件操作详谈
2007/03/19 PHP
浅析linux下apache服务器的配置和管理
2013/08/10 PHP
php curl模拟post请求小实例
2013/11/13 PHP
PHP使用zlib扩展实现GZIP压缩输出的方法详解
2018/04/09 PHP
PHP文件操作实例总结【文件上传、下载、分页】
2018/12/08 PHP
php项目中类的自动加载实例讲解
2019/09/12 PHP
用YUI做了个标签浏览效果
2007/02/20 Javascript
JS链式调用的实现方法
2013/03/07 Javascript
js的flv视频播放器插件使用方法
2015/06/23 Javascript
js获取url传值的方法
2015/12/18 Javascript
JS hashMap实例详解
2016/05/26 Javascript
jquery checkbox无法用attr()二次勾选问题的解决方法
2016/07/22 Javascript
JS实现类似51job上的地区选择效果示例
2016/11/17 Javascript
深入理解vue.js双向绑定的实现原理
2016/12/05 Javascript
使用webpack打包koa2 框架app
2018/02/02 Javascript
Vue实现textarea固定输入行数与添加下划线样式的思路详解
2018/06/28 Javascript
vue中实现动态生成二维码的方法
2020/02/21 Javascript
解决python2.7 查询mysql时出现中文乱码
2016/10/09 Python
Python基于QRCode实现生成二维码的方法【下载,安装,调用等】
2017/07/11 Python
详解python3中tkinter知识点
2018/06/21 Python
python中sys.argv函数精简概括
2018/07/08 Python
使用Python3+PyQT5+Pyserial 实现简单的串口工具方法
2019/02/13 Python
自定义django admin model表单提交的例子
2019/08/23 Python
Python迭代器Iterable判断方法解析
2020/03/16 Python
python 画条形图(柱状图)实例
2020/04/24 Python
Python自动化办公Excel模块openpyxl原理及用法解析
2020/11/05 Python
python搜索算法原理及实例讲解
2020/11/18 Python
澳大利亚宠物食品和药物在线:Jumbo Pets
2018/03/24 全球购物
Casetify官网:自制专属手机壳、iPad护壳和Apple Watch手表带
2018/05/09 全球购物
俄罗斯品牌服装和鞋子在线商店:BRIONITY
2020/03/26 全球购物
优秀学生党员先进事迹材料
2014/05/29 职场文书
2014年群众路线教育实践活动整改措施
2014/09/24 职场文书
中学生运动会广播稿
2015/08/19 职场文书
CSS3实现的侧滑菜单
2021/04/27 HTML / CSS
OpenCV-Python实现轮廓的特征值
2021/06/09 Python
JavaWeb 入门篇(3)ServletContext 详解 具体应用
2021/07/16 Java/Android