p-queue を使ってみた
仕事で、外部の API に対してリクエストを大量に送りたいということがあった。ただし、件数は約 3000 件。一度に大量にリクエストするわけにはいかないので1、同時に実行できる数に上限を設けたい。
自作してもいいなと思ったが、とりあえずライブラリを使うことにした。いろいろ調べたら p-queue がおもしろそうだったので、これで。
前提
- p-queue v7.1.0
- Node.js v14.18.0
- TypeScript v4.4.3
- esbuild v0.13.4
概要
p-queue は、その名のとおり、Promise のキューを作ってくれる。
サンプルプロジェクトは こちら 。
キューを作るときに同時実行数を指定する。
const queue = new PQueue({ concurrency: 2 });
add()
でキューに追加していく。最後に Promise.all()
で全ての Promise が完了するのを待つ。
(async () => {
const promises = [];
for (let i = 1; i <= 30; i++) {
promises.push(
queue.add(async () => {
const { data } = await axios.post(API_URL, { id: i });
console.log(data);
}),
);
}
await Promise.all(promises);
})();
実際に動かしてみると、最初に
{ message: 'Success: 1' }
{ message: 'Success: 2' }
が出て、少し経ってから
{ message: 'Success: 3' }
{ message: 'Success: 4' }
が出る。このように同時に 2 つまでしか実行されていないことが分かる。
備考
p-queue
は ES Modules (ESM) で作られている。一方、TypeScript はデフォルトで CommonJS (CJS) で出力する。
この不一致のせいで、実行時に以下のエラーが出力されてしまう。
internal/modules/cjs/loader.js:1102
throw new ERR_REQUIRE_ESM(filename, parentPath, packageJsonPath);
^
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/xxxx/pqueue-sample/node_modules/p-queue/dist/index.js
require() of ES modules is not supported.
require() of /Users/xxxx/pqueue-sample/node_modules/p-queue/dist/index.js from /Users/xxxx/pqueue-sample/dist/client.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/xxxx/pqueue-sample/node_modules/p-queue/package.json.
at new NodeError (internal/errors.js:322:7)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1102:13)
at Module.load (internal/modules/cjs/loader.js:950:32)
at Function.Module._load (internal/modules/cjs/loader.js:790:12)
at Module.require (internal/modules/cjs/loader.js:974:19)
at require (internal/modules/cjs/helpers.js:93:18)
at Object.<anonymous> (/Users/xxxx/pqueue-sample/dist/client.js:7:35)
at Module._compile (internal/modules/cjs/loader.js:1085:14)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
at Module.load (internal/modules/cjs/loader.js:950:32) {
code: 'ERR_REQUIRE_ESM'
}
p-queue
の作者は、この問題のために懇切丁寧な ドキュメント を用意してくれている。TypeScript で使う方法は How can I make my TypeScript project output ESM? に書かれている。
問題は、この長い手順をやりたいかどうかである。私には ESM はわからぬ。私は、一介のエンジニアである。仕事で渋々 TypeScript を書いているにすぎない。けれども面倒くささに対しては、人一倍に敏感であった。2
ということで、esbuild でどうにかすることにした。esbuild のビルドスクリプトに、以下のように書けば ESM で出力してくれる。
const esbuild = require('esbuild');
esbuild.buildSync({
entryPoints: ['src/client.ts'],
outdir: 'dist',
outExtension: { '.js': '.mjs' },
format: 'esm',
});
outExtension は、 .js
を出力するときに拡張子を自動的に ESM の .mjs
にする。これだけでも ESM で出力してくれるみたいだが、念の為、 format でも esm
を指定している。