场景


远古时代

我们在编写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通过async/await处理异步的更多相关文章

  1. [.NET] 利用 async & await 的异步编程

    利用 async & await 的异步编程 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/5922573.html  目录 异步编程的简介 异 ...

  2. [.NET] 利用 async & await 进行异步 IO 操作

    利用 async & await 进行异步 IO 操作 [博主]反骨仔 [出处]http://www.cnblogs.com/liqingwen/p/6082673.html  序 上次,博主 ...

  3. 利用 async & await 的异步编程

    走进异步编程的世界 - 开始接触 async/await 利用 async & await 的异步编程 async 的三大返回类型 公司技术需求备忘录

  4. async+await处理异步问题

    在编写网页的时候我们常常会遇到异步问题,async+await是es6提出的解决异步的方法,下面我们来看看这个方法怎么实现解决异步的, 大家都知道,setTimeout是一个定时器.他是一个异步执行的 ...

  5. Atitit. Async await 优缺点 异步编程的原理and实现 java c# php

    Atitit. Async await 优缺点 异步编程的原理and实现 java c# php 1. async & await的来源1 2. 异步编程history1 2.1. 线程池 2 ...

  6. 使用ES6新特性async await进行异步处理

    我们往往在项目中会遇到这样的业务需求,就是首先先进行一个ajax请求,然后再进行下一个ajax请求,而下一个请求需要使用上一个请求得到的数据,请求少了还好说,如果多了,就要一层一层的嵌套,就好像有点c ...

  7. 深入理解协程(三):async/await实现异步协程

    原创不易,转载请联系作者 深入理解协程分为三部分进行讲解: 协程的引入 yield from实现异步协程 async/await实现异步协程 本篇为深入理解协程系列文章的最后一篇. 从本篇你将了解到: ...

  8. [原创.数据可视化系列之十二]使用 nodejs通过async await建立同步数据抓取

    做数据分析和可视化工作,最重要的一点就是数据抓取工作,之前使用Java和python都做过简单的数据抓取,感觉用的很不顺手. 后来用nodejs发现非常不错,通过js就可以进行数据抓取工作,类似jqu ...

  9. js async await 终极异步解决方案

    既然有了promise 为什么还要有async await ? 当然是promise 也不是完美的异步解决方案,而 async await 的写法看起来更加简单且容易理解. 回顾 Promise Pr ...

随机推荐

  1. 关联规则&Apriori算法

    2017-12-02 14:27:18 一.术语 Items:项,简记I Transaction:所有项的一个非空子集,简记T Dataset:Transaction的一个集合,简记D 关联规则: 一 ...

  2. Solaris11 Vsphere client tool 的鼠标问题

    Mouse does not function on a Solaris 11 virtual machine (2040498) http://kb.vmware.com/selfservice/m ...

  3. sql语句练习-基础篇

    本文内容源自改编http://blog.csdn.net/ochangwen/article/details/51297893, 针对mysql数据库做了语法更改 个人觉得原版有些不合理之处,改了部分 ...

  4. EF大数据批量添加性能问题

    前几天做一个批量发消息的功能,因为要向消息表中批量写入数据,用的EF框架的插入方法:不用不知道,一用吓一跳:就10000条数据就耗时好几分钟,对应追求用户体验的我来说这是极不能容忍的,后来改为拼接SQ ...

  5. 13个能快速开发android的经典项目

    一.okhttp一个让网络请求更简单的框架 项目地址 https://github.com/jeasonlzy/okhttp-OkGo 二. TwinklingRefreshLayout-下拉刷新和上 ...

  6. Appium 自动化测试(7) -- Appium 服务器初始化参数设置

    Desired Capabilities Desired capabilities 是一些发送给 Appium 服务器的键值对集合 (比如 map 或 hash),告诉服务器我们想要启动什么类型的自动 ...

  7. 【解决】Android 2.x 不支持overflow、position:fixed解决方案【转】

    Android 2.x和IOS5以下都不支持overflow:auto属性(position:fixed也不支持). 移动端浏览器兼容性和PC端相比,有过之而无不及.操作系统版本及各式浏览器和各式的屏 ...

  8. 《转》深入理解Activity启动流程(二)–Activity启动相关类的类图

    本文原创作者:Cloud Chou. 出处:本文链接 本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究. 在介绍Activity的详细启动流程之前,先为大家介绍Act ...

  9. New Concept English Two 8 19

    pls  practice every day $课文17 青春常驻 161. My aunt Jennifer is an actress. 我的姑姑詹妮弗是位演员, 162. She must b ...

  10. [置顶] Unity2d引入新功能SpriteAtlas,Sprite新的图集方式

    孙广东  2017.8.3 http://blog.csdn.NET/u010019717 在Unity 2017.1.0f3中引入了 SpriteAtlas 官方文档:  https://docs. ...