原文来自语雀专栏:www.yuque.com/egg/nodejs/…javascript
做者:苏千、天猪html
npminstall 是 cnpm 的核心逻辑库之一,它经过 link 的方式来安装 Node.js 依赖,能够极大的提高安装速度。java
回顾 npminstall 初版的代码,默认支持 Node.js 4,那个时候 async/await
还没成为 Node.js 的默认功能,各类三方库仍是 callback 接口。因此咱们选择基于 co/generator
模式来开发,避免 Callback Hell。node
时光如梭,Node.js 已经发布了 12.x 版本,ES6 早已普及,async/await
早已经在 Node.js 8 就默认开启,因此咱们决定给 npminstall 进行一次大重构,完全拥抱 async/await
,跟 co/generator
说再见。git
再次感谢 TJ,让咱们提早好多年就享受着 async/await 般的编码体验。github
这是最容易的替换,几乎能够无脑全局替换。npm
function*
=> async function
yield
=> await
老代码:c#
module.exports = function* (options) {
// ...
yield fn();
};
复制代码
新代码:promise
module.exports = async options => {
// ...
await fn();
};
复制代码
值得关注的是并发执行的任务,在 co/generator
模式下只须要 yield tasks
便可实现,而 async/await
模式下须要明确地使用 Promise.all(tasks)
来声明。并发
老代码:
const tasks = [];
for (const pkg of pkgs) {
tasks.push(installOne(pkg));
}
yield tasks;
复制代码
新代码:
const tasks = [];
for (const pkg of pkgs) {
tasks.push(installOne(pkg));
}
await Promise.all(tasks);
复制代码
它能够替代 Promise.all() 且提供并发数限制能力。
最大的思惟差异是 async function
立刻开始执行,而 generator function
是延迟执行。
老代码:
const parallel = require('co-parallel');
for (const childPkg of pkgs) {
childPkg.name = childPkg.name || '';
rootPkgsMap.set(childPkg.name, true);
options.progresses.installTasks++;
tasks.push(installOne(options.targetDir, childPkg, options));
}
yield parallel(tasks, 10);
复制代码
新代码:
在 mapper
被调用的时候才会真实执行。
const pMap = require('p-map');
const mapper = async childPkg => {
childPkg.name = childPkg.name || '';
rootPkgsMap.set(childPkg.name, true);
options.progresses.installTasks++;
await installOne(options.targetDir, childPkg, options);
};
await pMap(pkgs, mapper, 10);
复制代码
mz-modules 和 mz 是咱们用的比较多的 2 个模块。
const { mkdirp, rimraf, sleep } = require('mz-modules');
const { fs } = require('mz');
async function run() {
// 非阻塞方式删除目录
await rimraf('/path/to/dir');
// +1s
await sleep('1s');
// 非阻塞的 mkdir -p
await mkdirp('/path/to/dir');
// 读取文件,请把 `fs.readFileSync` 从你的头脑里面完全遗忘。
const content = await fs.readFile('/path/to/file.md', 'utf-8');
}
复制代码
fs-extra 已经默认支持 async/await,不须要再使用 co 包装一层。
老代码:
const fse = require('co-fs-extra');
yield fse.emptyDir(targetdir);
复制代码
新代码:
const fse = require('fs-extra');
await fse.emptyDir(targetdir);
复制代码
node-modules/runscript 用于执行一条指令。
const runScript = require('runscript');
async function run() {
const { stdout, stderr } = await runScript('node -v', { stdio: 'pipe' });
}
复制代码
重构后总体代码量其实并不会变化太大,几乎是等价的代码量。有一些须要特别回顾的注意点:
Promise.all()
。