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

Github.com/sindresorhu…

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 assistancePromise.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.