質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

7395閲覧

JavaScript 非同期処理とforを組み合わせた書き方が分からない

Takiro

総合スコア7

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

1クリップ

投稿2017/05/17 10:22

編集2017/05/17 13:55

###前提
今、JavaScriptの非同期処理について学んでいます。
for文と非同期処理をうまく書く方法を探していたところ、とあるウェブサイトで以下のようなコードを見つけました。
(参考URL:javascriptで非同期処理をfor文で回したい。)

しかし、内容が理解出来ません。

初心者なので、当たり前のことを理解していないとんちんかんな質問になっているかもしれませんが、どうぞよろしくお願いします。

例えば、次のような非同期処理を上から順に行って行きたい時(a,b,c,dを順番に表示したい)、下のように書き換えればa,b,c,dの順に表示されるようになります。

JavaScript

1setTimeout( () =>{console.log('a');},4000); 2setTimeout( () =>{console.log('b');},3000); 3setTimeout( () =>{console.log('c');},2000); 4setTimeout( () =>{console.log('d');},1000);

JavaScript

1(function(){ 2var arr = ['a','b','c','d']; 3var d = Promise.resolve(); 4for(var i=0;i<arr.length;i++){ 5 d = d.then( function(i){ 6 return new Promise((resolve,reject)=>{ 7 setTimeout(()=>{ 8 console.log(arr[i]); 9 resolve(); 10 } 11 ,(4-i)*1000) 12 }) 13 }.bind(null,i)) 14} 15})();

###疑問点
①Promise.resolve()は、then()メソッドを呼び出すためにPromiseオブジェクトを返す役割を果たしていると解釈してよいのでしょうか。
②forを展開した時にどういう記述になるのか分かりません。あるいは、forが進むごとにどのような処理が具体的に行われているのか分かりません。d = d.then(以下略)とすることでiのカウントが進むごとにthenチェーンがつながるのかな程度の想像で理解が止まってしまいます(そもそもその想像自体がまちがっているかもしれませんが)。
③即時関数への引数をbindで指定していますが、なぜそうする必要があるのかが分かりません。実際、.bind(null, i)の部分を(i)に変えたところ、d,c,b,aの順に表示されてしまいました。コールバック関数の引数を固定する場合bindを使うようですが、そもそもその理由が理解できていません。bindの基本的な使い方としてthisの対象を固定するというのは理解したのですが、引数について分からないままです……。

以上の点、お答え頂ける優しい方がいたらどうかよろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2017/05/17 10:43

引用元(URL)を開示して下さい。また、引用文と質問者さん本人の文章の見分けがつかないので、引用箇所は行頭に "> " を付けてmarkdown記法で書いて頂けると、読む人の誤解を生まなくて良いと思います。
Takiro

2017/05/17 12:05

大変失礼しました。引用元を明記し、引用箇所に>をつけました。ご指摘ありがとうございました。
think49

2017/05/17 13:43

``` の行頭にも "> " を付けて下さい。そうすれば、blockquote>pre>code でマークアップされます。
Takiro

2017/05/17 13:56

引用箇所の修正を行いました。引用の方法について大変参考になりました。追加のご指摘ありがとうございました。
guest

回答2

0

再帰処理 (Promise + setTimeout)

  • ES6 Promiseの場合

JavaScript

1(function(){ 2var arr = ['a','b','c','d']; 3var d = Promise.resolve(); 4for(var i=0;i<arr.length;i++){ 5 d = d.then( function(i){ 6 return new Promise((resolve,reject)=>{ 7 setTimeout(()=>{ 8 console.log(arr[i]); 9 resolve(); 10 } 11 ,(4-i)*1000) 12 }) 13 }.bind(null,i)) 14} 15})();

元記事のコードをリファクタリングすると、次のコードになります。

JavaScript

1let p = Promise.resolve(); 2for (const [key, value] of ['a','b','c','d'].entries()) { 3 p = p.then((resolve) => new Promise((resolve) => { 4 return setTimeout((value) => { 5 console.log(value); 6 resolve(); 7 }, 4000 - 1000 * key, value); 8 })); 9}

p = p.then(); で代入を繰り返す事で p.then().then().then().then() を実現しています。

再帰処理 (setTimeout)

しかしながら、この例なら Promise を使うまでもないと私は思います。

JavaScript

1setTimeout(function handleTimeout (array, index) { 2 console.log(array[index++]); 3 4 if (index < array.length) { 5 setTimeout(handleTimeout, 4000 - 1000 * index, array, index); 6 } 7}, 4000, ['a','b','c','d'], 0);

Re: Takiro さん

投稿2017/05/17 14:19

編集2017/05/17 14:30
think49

総合スコア18164

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

think49

2017/05/17 14:32

質問の本題とは関係なさそうですが、setTimeout の第二引数が元コードと同一の挙動ではなかったので修正しました。
Takiro

2017/05/18 03:49

ご回答ありがとうございます。配列をイテレーターオブジェクトにしてfor...ofというこのような書き方が可能になるのですね。かなり見やすくなるので参考になりました。今後この書き方をしてみたいと思います。 それとコールバック関数を使った再帰の方法ですが、確かに簡単なものならpromiseを使わずこちらの方がよさそうです。
guest

0

ベストアンサー

①Promise.resolve()は、then()メソッドを呼び出すためにPromiseオブジェクトを返す役割を果たしていると解釈してよいのでしょうか。

はい

②forを展開した時に...

ご想像の通り、then をつなげているだけです。
bind でわかりづらくなっていますが、

javascript

1d = d.then(() => new Promise((resolve, reject) => {setTimeout うんぬん})

です。

③即時関数への引数をbindで指定していますが、...

bind は元の関数のカリー化された新しい関数を作成して返すものです。第一引数は this, それ以降は関数の引数に紐付きます。

javascript

1const fun1 = function(x, y) { 2 console.log(this, x, y); 3}; 4 5const fun2 = fun1.bind(1, 2, 4); 6fun2(); // [Number: 1] 2 4

このようになります。

そのため

javascript

1function(i){...}.bind(null, i) // () => {...} thisはnull

となります。

投稿2017/05/17 11:25

TakeoAsai

総合スコア880

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Takiro

2017/05/17 12:35 編集

ご回答ありがとうございます。分かりやすい説明で大変助かりました。 ただどうしても気になる点が残ってしまいます。 もし時間に余裕があればでいいので、お答えいただければと思います。 ②について:thenがつながっていく処理をしていることは分かりましたが、d = d.thenのdの部分がなかなか理解出来ません。なぜ変数に処理を入れてその部分で実行されるのか、というのが疑問点です。 感覚としては、関数式の定義みたいな感じなのでしょうか。 var d = function(){}; //ここでdという関数を定義 d(); //dを実行 上の例で言えば、 d = d.then(以下略)がdを実行する部分ということになるのでしょうか。 ③について:bindで引数を固定するというのは分かりましたが、.bind(null, i)の部分を(i)にして即時関数の書き方にした場合挙動が変わってしまうのはなぜでしょうか。 初歩的な質問ばかりかと思うので、参考となるURLを貼っていただけるだけでもありがたいです。 よろしくお願いします。
TakeoAsai

2017/05/17 13:15

> d = d.then(以下略)がdを実行する部分ということになるのでしょうか。 いいえ。 d = d.then(...) で作られるのは、 Promise.resolve().then(...).then(...).then(...).then(...) のようなオブジェクトです。実行はまだしません。 > 即時関数 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise/then 即時関数にすると、thenの第一引数が関数ではなくなるため > 成功状態の Promise を作成して返します。 とあります。(i)とした即時関数がPromiseを返しても、それを待たず成功状態の Promiseでthenをつなぐのでおかしくなります。
Takiro

2017/05/17 13:40

分かりやすいご回答、重ね重ねありがとうございます。d = d.then(...)の部分と即時関数に関して、すっきりと理解することが出来ました。 しかし、ここでまた疑問が生じてしまいます。Promise.resolve().then(...).then(...).then(...).then(...)がfor文によって再帰的にdに代入された後、この部分は一体どこで実行されるのでしょうか。 質問が何度にもわたってしまって申し訳ありません。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問