From the original language finches column: www.yuque.com/egg/nodejs/…
Authors: Su Qian, Tianzhu
A simple review
Npminstall is one of the core logical libraries of CNPM. It installs Node.js dependencies through link, which can greatly improve the installation speed.
Looking back at the first version of Npminstall, node.js 4 was supported by default. At that time async/await was not the default feature of Node.js, and various tripartite libraries were callback interfaces. So we chose to develop in CO/Generator mode to avoid Callback Hell.
Node.js was released in 12.x, ES6 was already popular, async/await was already enabled by default in Node.js 8, so we decided to give Npminstall a big refactoring to embrace async/await completely. Say goodbye to CO/Generator.
Thanks again TJ for letting us enjoy the async/await coding experience many years in advance.
Turn the generator async
This is the easiest substitution, almost mindlessly global substitution.
function*
= >async function
yield
= >await
The old code:
module.exports = function* (options) {
// ...
yield fn();
};
Copy the code
The new code:
module.exports = async options => {
// ...
await fn();
};
Copy the code
Promise.all()
Note that concurrent tasks can be implemented in CO/Generator mode with yield tasks, while async/await mode requires explicit Promise. All (tasks) declaration.
The old code:
const tasks = [];
for (const pkg of pkgs) {
tasks.push(installOne(pkg));
}
yield tasks;
Copy the code
The new code:
const tasks = [];
for (const pkg of pkgs) {
tasks.push(installOne(pkg));
}
await Promise.all(tasks);
Copy the code
Common modules
co-parallel => p-map
It replaces promise.all () and provides concurrency limitation.
The biggest mental difference is that async Function starts execution immediately, whereas generator Function is deferred.
The old code:
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);
Copy the code
The new code:
This is actually executed when mapper is called.
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);
Copy the code
mz-modules
Mz-modules and Mz are the two modules we use most.
const { mkdirp, rimraf, sleep } = require('mz-modules');
const { fs } = require('mz');
async function run() {
// Delete directories in non-blocking mode
await rimraf('/path/to/dir');
// +1s
await sleep('1s');
// non-blocking mkdir -p
await mkdirp('/path/to/dir');
// Read the file, please put 'fs.readfilesync' out of your mind completely.
const content = await fs.readFile('/path/to/file.md'.'utf-8');
}
Copy the code
co-fs-extra => fs-extra
Fs-extra already supports async/await by default and does not need another layer of CO wrapping.
The old code:
const fse = require('co-fs-extra');
yield fse.emptyDir(targetdir);
Copy the code
The new code:
const fse = require('fs-extra');
await fse.emptyDir(targetdir);
Copy the code
runscript
Node-modules /runscript is used to execute an instruction.
const runScript = require('runscript');
async function run() {
const { stdout, stderr } = await runScript('node -v', { stdio: 'pipe' });
}
Copy the code
yieldable => awaitable
- We’ve also compiled a more detailed guide to Yiedable-to-Awaitable in Egg 1.x when upgrading 2.x:
- See also: promise-fun This repository.
conclusion
The total amount of code does not change much after refactoring, but is almost equivalent. There are some special points of note that need to be reviewed:
- Async functions are executed immediately when invoked, unlike Generator functions which are actually executed at yield.
- Concurrent execution requires assistance
Promise.all()
。 - You need to master some common auxiliary libraries, such as P-Map, MZ, mZ-Modules, etc.
- Don’t be afraid to use try catch, which works well.
- You may never need to use the CO module again.