Kuzunoha-NEのブログ

プログラミングなどの勉強をしてます

【TypeScript】Promise.allで頑張る

こんばんは。今回はPromise.allを使ってみます。MongoDBも使います。

developer.mozilla.org

記載の通り、配列に入ったPromiseを全部解決するまで待つという動きが出来るようになります。

Promise.allを使わないやりかた

import {MongoClient, MongoClientOptions} from "mongodb";

const uri:string = "mongodb://127.0.0.1:27017"
const options:MongoClientOptions = {useUnifiedTopology:true,useNewUrlParser:true}
const client = new MongoClient(uri,options);
const dbName = "__test__";
const collectionName = "test_collec";

const initing = async (client:MongoClient) => {
  await client.connect();
  const __test__ = client.db(dbName);
  const cursor = __test__.collection(collectionName).find();

  const datas = [];
  while(await cursor.hasNext()){
    datas.push(await cursor.next());
  };
  console.log(datas);

  await client.close();
  return ;
};

initing(client);

今回の話で最も重要なところはここの部分になります。

const datas = [];
while(await cursor.hasNext()){
  datas.push(await cursor.next());
};
console.log(datas);

datas配列にDBから取得してきたデータを挿入してきているのですが、DBから取得してきている間はawaitしているので、その間の処理は完全に止まっています。

Promise.allを使ったやり方

import {MongoClient, MongoClientOptions} from "mongodb";

const uri:string = "mongodb://127.0.0.1:27017"
const options:MongoClientOptions = {useUnifiedTopology:true,useNewUrlParser:true}
const client = new MongoClient(uri,options);
const dbName = "__test__";
const collectionName = "test_collec";

const initing = async (client:MongoClient) => {
  const cli = await client.connect();
  const __test__ = cli.db(dbName);
  const cursor = __test__.collection(collectionName).find();

  
  const datas = [];
  const count = await cursor.count();
  for (let i = 1; i <= count; i++){
    datas.push(cursor.next());
  };
  const promised = await Promise.all(datas);

  console.log(promised);

  await cli.close();
  return ;
};

initing(client);

変更部分は主にここ

const datas = [];
const count = await cursor.count();
for (let i = 1; i <= count; i++){
  datas.push(cursor.next());
};
const promised = await Promise.all(datas);
console.log(promised);

while文をやめて、for文にしています。これはwhile内にあったcursor.hasNext()の結果を待つという処理をやめて、単純にデータ内にある個数分データを取得するという判定に変更しています。そのfor文内で行っているdatas.push()は、cursor.next()に対して結果の取得を待つという処理がなされていません。これらを取得するまでに待っていたものはデータの個数を持っているcursor.count()だけです。

- while(await cursor.hasNext()){
-  datas.push(await cursor.next());
- };
+ const count = await cursor.count();
+ for (let i = 1; i <= count; i++){
+  datas.push(cursor.next());
+ };

datasの中身は[Promise { <pending> }, Promise { <pending> }, Promise { <pending> }]と言った感じになります。

そして、Promise.all()でPromiseだらけのdatasの取得を待ちます。この取得待ちは並列で行われるので、取得の待ち時間が少なくて住みます。そして、その結果をpromised変数に代入してあげます。

ソースコード

今回のソースコードはこちら。MongoDBをローカルで建ててない人向けにDockerも作っています。

github.com

このように並列処理を書くのだ

沢山のPromiseがあるときは、一旦全部を配列に入れてあげて、Promise.allを使ってあげると便利に良さそう。