[解決済み] JavaScript ES6 promise for loop [重複]について
質問
for (let i = 0; i < 10; i++) {
const promise = new Promise((resolve, reject) => {
const timeout = Math.random() * 1000;
setTimeout(() => {
console.log(i);
}, timeout);
});
// TODO: Chain this promise to the previous one (maybe without having it running?)
}
上記を実行すると、以下のようなランダムな出力が得られます。
6
9
4
8
5
1
7
2
3
0
作業は簡単です。各プロミスが他のプロミスの後にのみ実行されることを確認する (
.then()
).
なぜか、やり方が見つからなかった。
ジェネレータ関数(
yield
)、プロミスを返す単純な関数を試してみましたが、結局いつも同じ問題に帰着します。
ループが同期である
.
と
非同期
私なら単純に
async.series()
.
どのように解決するのですか?
どのように解決するのですか?
あなたの質問ですでに示唆されているように、あなたのコードはすべてのプロミスを同期的に作成します。代わりに、それらは直前のものが解決される時にのみ作成されるべきです。
次に
new Promise
を呼び出して解決する必要があります。
resolve
(または
reject
). これは、タイマーが切れたときに実行する必要があります。これは、すべての
then
のコールバックは、そのプロミスで持っているはずです。そして、そのような
then
コールバック(または
await
) は、チェーンを実装するために必要です。
これらの材料を用いて、この非同期チェインを実行する方法はいくつかあります。
-
を使用すると
for
即座に解決されるプロミスで始まるループ -
と
Array#reduce
で始まり、すぐに解決するプロミス -
解決コールバックとして自分自身を渡す関数を持つ
-
ECMAScript2017 の
async
/await
シンタックス -
ECMAScript2020 の
for await...of
構文
しかし、最初にとても便利な汎用的な機能を紹介しよう。
プロミシング
setTimeout
使用方法
setTimeout
でも良いのですが、実際にはタイマーが切れたときに解決するプロミスが必要です。そこで、そのような関数を作ってみましょう。
プロミス化
をプロミス化します。
setTimeout
. これはコードの可読性を向上させ、上記のすべてのオプションに使用することができます。
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
以下、各オプションのスニペットとコメントをご覧ください。
1. を使って
for
あなた
できる
を使用します。
for
しかし、それがすべての約束を同期的に作成しないことを確認する必要があります。その代わりに、すぐに解決する最初の約束を作り、そして前の約束が解決するにつれて新しい約束を連鎖させます。
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
for (let i = 0, p = Promise.resolve(); i < 10; i++) {
p = p.then(() => delay(Math.random() * 1000))
.then(() => console.log(i));
}
つまり、このコードでは
then
を呼び出します。変数
p
は、その連鎖を見失わないようにし、次のループの反復で同じ連鎖を続けられるようにするためだけのものです。コールバックは同期ループが完了した後に実行を開始する。
重要なのは
then
-コールバック
を返します。
という約束は
delay()
を作成します。これにより、非同期な連鎖が保証されます。
2. と
reduce
これは、先ほどの戦略をより機能的にしただけのものです。実行したいチェーンと同じ長さの配列を作成し、すぐに解決できるプロミスで開始します。
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
[...Array(10)].reduce( (p, _, i) =>
p.then(() => delay(Math.random() * 1000))
.then(() => console.log(i))
, Promise.resolve() );
これは、おそらく実際に使用するときに便利です。 ある プロミスで使用されるデータを持つ配列。
3. 解決コールバックとして自分自身を渡す関数を使用する場合
ここでは、関数を作成し、すぐに呼び出します。この関数は最初の約束を同期的に作成します.それが解決されると、関数が再び呼び出されます。
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
(function loop(i) {
if (i >= 10) return; // all done
delay(Math.random() * 1000).then(() => {
console.log(i);
loop(i+1);
});
})(0);
という名前の関数が作成されます。
loop
そして、コードの最後のほうで、引数 0 ですぐに呼び出されるのがわかります。 これはカウンタであり
i
引数で指定します。この関数は、カウンタがまだ10以下であれば新しいプロミスを作成し、そうでなければ連鎖を停止します。
いつ
delay()
が解決されると、それがきっかけで
then
コールバックは、この関数を再度呼び出します。
4. と
async
/
await
最新のJSエンジン はこの構文をサポートしています。 :
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
(async function loop() {
for (let i = 0; i < 10; i++) {
await delay(Math.random() * 1000);
console.log(i);
}
})();
というように、奇妙に見えるかもしれません。
と思われる
は、プロミスが同期的に作成されるように見えますが、実際には
async
機能
を返します。
を実行したとき、最初の
await
. 待ち受けが解決されるたびに,関数の実行コンテキストが復元され,その後に続く
await
そして、次のループに出会うまで、ループが終了するまで続けられる。
5. と
for await...of
EcmaScript 2020 では
for await...of
は、最新のJavaScriptエンジンに採用されました。この場合、コードを減らすことはできませんが、乱数区間の連鎖の定義と実際の反復を分離することができます。
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
async function * randomDelays(count, max) {
for (let i = 0; i < count; i++) yield delay(Math.random() * max).then(() => i);
}
(async function loop() {
for await (let i of randomDelays(10, 1000)) console.log(i);
})();
関連
-
fetch ネットワークリクエストラッパーの説明例
-
VUEグローバルフィルターの概念と留意点、基本的な使い方
-
[解決済み】Node.js getaddrinfo ENOTFOUND
-
[解決済み] JavaScriptで "use strict "は何をするのか、その根拠は?
-
[解決済み] JavaScriptで文字列が部分文字列を含むかどうかを確認する方法は?
-
[解決済み] あるJavaScriptファイルを他のJavaScriptファイルにインクルードするにはどうすればよいですか?
-
[解決済み] JavaScriptのオブジェクトが空であることをテストするにはどうすればよいですか?
-
[解決済み] JavaScriptのオブジェクトをループスルーまたは列挙するにはどうすればよいですか?
-
[解決済み】JavaScriptの比較では、どちらの等号演算子(== vs ===)を使うべきですか?
-
[解決済み】オブジェクトからプロパティを削除する(JavaScript)
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
親子コンポーネント通信を解決する3つのVueスロット
-
vue for 登録ページ効果 vue for sms 認証コードログイン
-
Vueのイベント処理とイベントモディファイアの解説
-
[解決済み】Node.js getaddrinfo ENOTFOUND
-
[解決済み】TypeError: Router.use() はミドルウェアの関数を要求しているが、Object を取得した。
-
[解決済み] Web API エラー - このリクエストはブロックされました; コンテンツは HTTPS で提供されなければなりません
-
[解決済み】TypeScript-のAngular Frameworkエラー - "exportAsがngFormに設定されたディレクティブはありません"
-
[解決済み】 Uncaught TypeError : undefined のプロパティ 'replace' を読み取れない In Grid
-
モジュールのビルドに失敗しました。Error: ENOENT: no such file or directory, scandir 'D:\.... \node_modules
-
[解決済み] 約束事を次々と解決していく(=順番に解決していく)?