3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

やらかしJS先生がforEachを大嫌いになった話❌

Posted at

#Overview

過去にバグになってしまったものを忘れないよう書き留めておくシリーズです。
今回の題材はArray.prototype.forEach()です。以下は私が当時のコードを思い出しながらさらり書いたコードです。(動作未確認)
複数のサイトのHTMLを取得して、ファイル出力しています。


import fs from "fs";
const fsPromises = fs.promises;

const writeFile = async (filename, json) => {
	const callback = err => { if (err) console.error(err); };
	await fsPromises.writeFile(filename, JSON.stringify(json), callback);
}

async function crawl() {
	const result = {};
	["https://www.google.co.jp/", "https://www.yahoo.co.jp/"].forEach(async (url) => {
		const res = await fetch(url);
		result[url] = await res.text();
	})

	await writeFile("urls.json", result)
	console.log("result:", result);
}
crawl();

#Target reader

  • この結果がわからない方

#Prerequisite

  • JavaScriptを一通り理解している
  • 出力結果はChromeで実行したが、環境によって出力内容が微妙に異なることがあることに注意。
  • 実行環境はNode V10系。(2020年7月時点で既にちょい古め)

#Body

##答え合わせ

正解はこうなる。

terminal
> "result:" Object {  }

何も出ないが正解:joy:
出力されたファイルの中身も同様に空のオブジェクト。

どうして何もないっていないかというと、forEachでawaitがないため。
awaitがないということはawaitの部分でファイル出力の方に処理が進んでしまう。
ではawaitいれればと思うが、実はこのawaitは意味がない。
詳しくは@frameairさんが書かれている記事を参照してください。
https://qiita.com/frameair/items/e7645066075666a13063

解決策はPromise.all()、もしくは一つずつ処理しないといけない場合はfor...ofで一つずつawaitすればいい。
Promise.all()はエラーの際に扱いにくいため、Promise.allSettled()がこれからはよさげ。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled

私がforEachを嫌いになった理由は、非同期処理があるかないかで書き方を変更しないといけないのが面倒だから。
極めつけとしては、asyncを入れても実行時エラーにならないのがつらい:sob:
ESLintで静的チェック、もしくはテストコードでカバーしておけばいいといえばそれまでですが:sweat_smile:

#Conclusion

forEachはawaitできないため、非同期処理を何気なく追加したらバグになる可能性がある!
forEachを利用するなら、非同期処理が入ったときにPromise.all()Promise.allSettled()もしくはfor...ofに切り替えること。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?