原文來自語雀專欄: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()
。