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 相关文章推荐
PHPStorm 2020.1 调试 Nodejs的多种方法详解
Sep 17 NodeJs
Nodejs进程管理模块forever详解
Jun 01 NodeJs
nodejs获取本机内网和外网ip地址的实现代码
Jun 01 NodeJs
轻松创建nodejs服务器(7):阻塞操作的实现
Dec 18 NodeJs
nodejs URL模块操作URL相关方法介绍
Mar 03 NodeJs
NodeJS实现阿里大鱼短信通知发送
Jan 17 NodeJs
Nodejs中 npm常用命令详解
Jul 04 NodeJs
浅谈Nodejs应用主文件index.js
Aug 28 NodeJs
Nodejs进阶:核心模块net入门学习与实例讲解
Nov 21 NodeJs
NodeJS爬虫实例之糗事百科
Dec 14 NodeJs
nodejs 简单实现动态html的方法
May 12 NodeJs
详解NodeJs开发微信公众号
May 25 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 操作excel文件的方法小结
2009/12/31 PHP
mysql下创建字段并设置主键的php代码
2010/05/16 PHP
php pki加密技术(openssl)详解
2013/07/01 PHP
支持中文的PHP按字符串长度分割成数组代码
2015/05/17 PHP
PHP抓取远程图片(含不带后缀的)教程详解
2016/10/21 PHP
PHP使用PDO操作sqlite数据库应用案例
2019/03/07 PHP
PHP使用PDO、mysqli扩展实现与数据库交互操作详解
2019/07/20 PHP
用js怎么把&字符换成"&amp:"
2006/10/19 Javascript
Jquery 最近浏览过的商品的功能实现代码
2010/05/14 Javascript
EXTjs4.0的store的findRecord的BUG演示代码
2013/06/08 Javascript
html+js实现动态显示本地时间
2013/09/21 Javascript
js对图片base64编码字符串进行解码并输出图像示例
2014/03/17 Javascript
JavaScript对数组进行随机重排的方法
2015/07/22 Javascript
javascript滚轮控制模拟滚动条
2016/10/19 Javascript
js实现仿购物车加减效果
2017/03/01 Javascript
Bootstrap免费字体和图标网站(值得收藏)
2017/03/16 Javascript
Node.JS利用PhantomJs抓取网页入门教程
2017/05/19 Javascript
Nuxt升级2.0.0时出现的问题(小结)
2018/10/08 Javascript
webpack中如何使用雪碧图的示例代码
2018/11/11 Javascript
[52:14]VG vs Serenity 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/20 DOTA
Python正则抓取网易新闻的方法示例
2017/04/21 Python
pygame游戏之旅 添加游戏界面按键图形
2018/11/20 Python
python中web框架的自定义创建
2019/09/08 Python
django xadmin action兼容自定义model权限教程
2020/03/30 Python
纯CSS3实现地球自转实现代码(图文教程附送源码)
2012/12/26 HTML / CSS
葡萄牙鞋子品牌:Fair
2016/12/10 全球购物
意大利简约的休闲品牌:Aspesi
2018/02/08 全球购物
Kusmi茶美国官网:优质散叶茶和茶包
2019/10/13 全球购物
中学教师师德承诺书
2014/05/23 职场文书
分公司任命书
2014/06/06 职场文书
实习证明格式范文
2014/10/14 职场文书
行政答辩状范文
2015/05/21 职场文书
2015年机关作风和效能建设工作总结
2015/07/23 职场文书
建筑工程挂靠协议书
2016/03/23 职场文书
golang json数组拼接的实例
2021/04/28 Golang
MongoDB支持的索引类型
2022/04/11 MongoDB