使用async/await方式将node.js中的异步方法包装成形式上的同步函数

在使用node.js写一些后台逻辑的时候,经常要调用一些异步互调获取结果的方法,也有很多三方库提供的接口函数是如此。
这里就拿node.js自己的文件模块来说,有个异步读取文件夹下文件列表的方法:

1
fs.readdir(path[, options], callback)

假设我们要调用readdir去获取文件列表时,例子如下:

1
2
3
4
var fs = require('fs');
fs.readdir(__dirname,(err,files)=>{
console.log('files:',files);
})

使用node.js,回调地狱是常被大家提起的一个词,这里,如果我们要对文件列表做后续处理,只能下载readdir的回掉函数
中了,这是个痛苦的经历。同时,我们也知道node.js的fs模块还提供了一个同步读取文件列表的方法:

1
fs.readdirSync(path[, options])

这样,使用起来, 直接写成下面这种方式即可:

1
2
3
var fs = require('fs');
let files = fs.readdirSync(__dirname);
console.log('files:',files)

那么,我们如果能把异步函数包装成同步,使用最新的Promise 下的async/await方式,在包装函数中,直到回掉函数返回后去
resolve(msg)就可以写出一个依旧是异步调用,但看起来是同步的处理逻辑,这里用fs.readdir举例子,测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
var fs = require('fs')

// 包装方法wrap,入参为待包装的异步函数
function wrap(func) {
//包装函数返回的新函数在执行时,将会返回一个Promise对象,
return function() {
return new Promise((resolve,reject) => {
arguments[arguments.length++] = function(err, ...rest) {
console.log('3333')
if(err) {
reject(err);
}
//异步回掉进入的时候将异步结果resolve回去
resolve(rest);
}
console.log('2222')
/此处可以看出包装函数在执行时实际上还是执行原来的异步函数func,只是对arguments做了修改
func.apply(this,arguments)
})
}
}

//将包装后的文件读取函数放入工具对象util
var util = {};
util.readdir = wrap(fs.readdir);

async function test() {
console.log('1111')
//由于util.readdir的返回值是个promise,必须使用await 方式接收才能拿到promise中的内容
let ret = await util.readdir(__dirname);
console.log('4444')
console.log('ret:',ret)
return ret;
}
console.log('befor test');
test().then(ses => console.log('rest:',ses));

这里在工具对象util下创建一个包装函数wrap,函数传入一个异步函数名,返回的新函数返回一个Promise对象,
对象中包裹被包装的异步函数在执行时,回掉函数的接收信息。由于目前低版本的node.js 还无法原生支持async/await,
需要使用Babel将ES2017的js语句转换成es5,然后由node.js解析。