promise-pool
やりたいこと
あるAPIを叩いて、データを取ってきたい。20個くらいAPIを叩く先があるけれども、10個ずつ取りにいって、1つの処理が終われば次のAPIを叩きたい。
1つずつ取ってくる場合(async await)
import axios from 'axios';
const first = async () => {
const start = performance.now();
const result1 = await axios.get('https://yahoo.co.jp');
const result2 = await axios.get('https://yahoo.co.jp');
const result3 = await axios.get('https://yahoo.co.jp');
const result4 = await axios.get('https://yahoo.co.jp');
const result5 = await axios.get('https://yahoo.co.jp');
const result6 = await axios.get('https://yahoo.co.jp');
const result7 = await axios.get('https://yahoo.co.jp');
const result8 = await axios.get('https://yahoo.co.jp');
const result9 = await axios.get('https://yahoo.co.jp');
const result10 = await axios.get('https://yahoo.co.jp');
const result11 = await axios.get('https://yahoo.co.jp');
const result12 = await axios.get('https://yahoo.co.jp');
const result13 = await axios.get('https://yahoo.co.jp');
const result14 = await axios.get('https://yahoo.co.jp');
const result15 = await axios.get('https://yahoo.co.jp');
const result16 = await axios.get('https://yahoo.co.jp');
const result17 = await axios.get('https://yahoo.co.jp');
const result18 = await axios.get('https://yahoo.co.jp');
const result19 = await axios.get('https://yahoo.co.jp');
const result20 = await axios.get('https://yahoo.co.jp');
const end = performance.now();
console.log({ result: end - start, name: 'first' }); //{ result: 3918.3598749637604, name: 'first' }
};
first();
超シンプルなasync awaitで取得してくるバージョンです。重いasync関数をひとつずつ処理していきます。遅いですね。上から1個ずつ取ってこないといけない、みたいな順番が大事なときには良いかもしれません。
一気に取りに行く場合(promise all)
import axios from 'axios';
const second = async () => {
const start = performance.now();
const [
result1,
result2,
result3,
result4,
result5,
result6,
result7,
result8,
result9,
result10,
result11,
result12,
result13,
result14,
result15,
result16,
result17,
result18,
result19,
result20
] = await Promise.all([
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp'),
axios.get('https://yahoo.co.jp')
]);
const end = performance.now();
console.log({ result: end - start, name: 'second' }); // { result: 331.03974986076355, name: 'second' }
};
second();
promise.allで一気に取りに行く方法です。これが一番早いといえば早いです。20件くらいならいけるかもしれませんが、叩きに行くAPIが貧弱だと100件とかになるとout of memoryが怖くなっていきます。あとは同時接続制限とかに引っかかる可能性もあります。それからエラーのときはどうするか……みたいなことを考えないといけません。allSettledなども検討しないと……。
Poolで同時10件の処理をする
import axios from 'axios';
import { PromisePool } from '@supercharge/promise-pool';
const third = async () => {
const start = performance.now();
const { results, errors } = await PromisePool.for([
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp',
'https://yahoo.co.jp'
]).process(async (urls) => {
return axios.get(urls);
});
const end = performance.now();
console.log({ result: end - start, name: 'third' }); // { result: 550.29279088974, name: 'third' }
};
third();
PromisePool
というライブラリを使うと、同時に10件まで接続して取ってきてくれます(10件の部分は変更できます)。10個まで同時に処理してくれて、1つ処理が終わったら11個目の処理をはじめてくれるという優れたライブラリです。返り値がresultsとerrorsなのもいいね!
最後にもう1回違いをおさらい。
仮に100個のasync関数があったとして……。
async await -> 1から100まで1個ずつ処理
promise.all -> 100個を一気に処理しようとする(たぶんメモリ足らん)
promise.allを10個ずつに分割-> 10個処理→10個が完全に終わる→次の10個の処理
promisePool -> 10個同時処理、1個終わったら11個めが自動で開始される。どんどん処理が進んでいくので早い!
という感じです。
どういうAPIをどれくらいの頻度で叩くのか、などを考えて取捨選択すればよろしいかと……。