NodeJs经过async/await处理异步

##场景

远古时代

咱们在编写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进行相应的显示或处理的。node

Promise时代

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

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()包裹起来,每个函数都有其独立的做用域,若是为了共享某个数据就必须挂在最外层,最重要的仍是,它与咱们熟悉的同步编程仍然有差异。web

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去驱动的,它和咱们想象中的函数多少有一点点不一样。express

async/await时代

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

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?json

## 使用

咱们一开始就说过,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里便可。

相关文章
相关标签/搜索