介绍 Node.js 未来两处很不错的新特性

1,307 阅读2分钟
原文链接: zhuanlan.zhihu.com

一、Promise 化的 fs API

就在几天前 Node.js 主干合入了一个巨大的 PR:

fs: add promises API by jasnell · Pull Request #18297 · nodejs/nodegithub.com图标

这个 PR 实验性地加入了 Promise 化的 fs API。虽然现在我们可以使用 util.promisify 来简单地封装 fs 这一套 API,但这只是“封装”。从 Node.js 内核层面上支持 Promise 会有更大的性能优势。

以后我们可以这样优雅的读取文件:

const { readFile } = require('fs').promises

async function readAndPrint(filename) {
    console.log(await readFile(filename))
}
readAndPrint('file')
 

错误处理也可以直接使用 try...catch 了,而不是用 Promise.catch

const { readFile } = require('fs').promises

try {
    await readFile('file')
} catch(e) {
    console.log(e)
}
 

二、支持 Async Iterators 的 stream API

Async Iterators 目前已经进入 stage-3 阶段,未来将会进入语言标准,V8 目前已经支持这个语言特性。先简单介绍一下 Async Iterators(异步迭代器)是什么。

ES6 的 Generator 函数大部分人应该都或多或少知道一些:

function* idMaker() {
    var index = 0;
    while(true)
        yield index++;
}

var gen = idMaker();

console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
// ...

简单地说,如果我们在 Generator 每次 yield 出的都是一个 Promise,那么它就成为了一个 Async Iterators:

function* asyncIdMaker() {
    var index = 0;
    while(true)
        yield Promise.resolve(index++);
} 

(关于 Generator 和 Iterator 的关系,可以参考这里)

针对 Async Iterators,可以使用专门的 for await 语法来操作:

for await (const id of asyncIdMaker()) {
    console.log(id)
} 

Node.js 的 stream API 在今年年初也实验性地加入了这个特性:

stream: added experimental support for for-await by mcollina · Pull Request #17755 · nodejs/nodegithub.com图标

在过去,我们使用 stream API 时一般都会写出类似这样的代码:

// 创建一个 stream
var myReadStream = fs.createReadStream('filename', 'utf-8')

// 响应 data 事件
myReadStream.on('data', function(chunk) {
    console.log(chunk);
    // 处理 chunk
}) 

这种基于事件回调的实现其实是不太优雅的,逻辑只要稍微复杂一些就很容易写出嵌套层次很深的代码。

现在我们可以使用 Async Iterators 这个新特性来处理:

// 创建一个 stream
var myReadStream = fs.createReadStream('filename', 'utf-8');

(async () => {
    for await (const chunk of myReadStream) {
        console.log(chunk);
        // 处理 chunk
    } 
})()

这样就减少了很多不必要的函数嵌套,并且让代码看起来更“同步”。