在node.js 中使用async await
一、历史
在用node.js编写程序代码时通常会遇到很多的异步操作,对于异步的处理,大概经历了以下几种方法:
1、回调。也就是第一个参数是error的函数,那么如果嵌套的层数多了,也就感觉特别非人类了,读起来会非常困难。
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);
}
});
}
})
}
});
2、Promise。我们也可以称作链式操作。从以下代码可以看出代码的可读性已经大大改善。但是我们仍然不可忽视某些问题,Promise 的最大问题是代码冗余,原来的任务被 Promise 包装了一下,不管什么操作,一眼看去都是一堆then,原来的语义变得很不清楚。
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);
})
3、Generator。借着ES6的Generator迭代器,最早实现了异步编程同步化的功能,也就是最为我们所熟知的co库。我们通过co(function *(){})可以使函数内部通过迭代器来控制。而co在这里则是充当了启动器的角色。
参考:Generator 函数的异步应用
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);
});
4、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,更符合我们异步编程的语义。
参考:async 函数
二、使用
babel已经支持async的transform了,所以我们使用的时候引入babel就行。
在开始之前我们需要引入以下的package,其中babel-plugin-transform-runtime是babel的一个支持es7async的插件。 es7不同阶段语法提案的转码规则共有4个阶段,分别是babel-preset-stage-0,babel-preset-stage-1,babel-preset-stage-2,babel-preset-stage-3。选装一个就行,我这里用的是babel-preset-stage-3。
npm install babel-core --save-dev
npm install babel-preset-es2015 --save-dev
npm install babel-preset-stage-3 --save-dev
npm install babel-plugin-transform-runtime --save-dev
我们使用官方提供的require hook方法,顾名思义就是通过require进来后,接下来的文件进行require的时候都会经过Babel的处理。因为我们知道CommonJs是同步的模块依赖,所以也是可行的方法。我们需要多一个用于启动的js文件,一个真正执行程序的js文件。
// index.js
// 用于引入babel,并且启动app.js
require("babel-core/register");
require("./app.js");
require("babel-core").transform("code", {
plugins: ["transform-runtime"]
});
配置完hook之后,我们就配置babel的.babelrc文件,它是一个在项目根目录的json格式的文件。(在windows系统中新建文件名为以.开头的文件,如.babelrc,只需将文件名写为.babelrc.即可)
{
"presets": [
"stage-3",
"es2015"
],
"plugins": [
[
"transform-runtime",
{
"helpers": false,
"polyfill": false,
"regenerator": true,
"moduleName": "babel-runtime"
}
]
]
}
我们的异步代码写在app.js中即可。