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

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

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

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

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

Q&A

解決済

1回答

381閲覧

どこで1回目と2回目を判定しているのかわからない

aaaaaaaa

総合スコア501

JavaScript

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

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

0グッド

2クリップ

投稿2018/08/28 09:54

下記のソースは、ブレイクスルーjavascript99頁のソースです。
ドキュメント要素をクリックするたびにコンソールが表示されるが、二回目以降は、1秒処理を遅らせるものです。

javascript

1//jquery 3.3.1 2var prevState = new $.Deferred().resolve().promise(); 3 4function asyncFuncDef() { 5 var deferred = new $.Deferred(); 6 setTimeout(function() { 7 deferred.resolve("done"); 8 }, 1000); 9 return deferred.promise(); 10} 11 12$(document).on("click",function() { 13 prevState = prevState.then(function() { 14 console.log("done"); 15 return asyncFuncDef(); 16 }); 17});

ソースのどのあたりで、一回目と二回目を判別しているのでしょうか。
一見するとクリックされるとクリックで発火するイベントリスナーが実行され、asyncFuncDef()が返値で実行され、またクリックされるとクリックで発火するイベントリスナーが実行され、asyncFuncDef()が返値で実行され…
と一回のクリックでdoneが二回表示されるようにしか見えません。しかし、実際に実行するとそうではありませんでした。

どこで1回目と2回目を判定しているのでしょうか。

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

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

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

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

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

guest

回答1

0

ベストアンサー

追記あり。

1回目と2回目を判別しているというよりは、「次の処理を1秒待たせている」という解釈が正しいです。
asyncFuncDefで返ってくるのはpromiseオブジェクトですが、この関数中で新しいdefferedオブジェクトを作成し、そのdefferedの中の新しいpromiseを返します。
この返ってきたpromiseオブジェクトの状態は、returnした時点ではpendingです。
このpending状態は直上の処理のsettimeoutにより、1000ms後にdoneになります。
doneにならないと次のthen以降の処理が実行されません。
よって、クリックされたときの処理が前の処理の実行より1秒待たされることになります。

なお、deferred.resolve("done");の"done"ですが、これはconsole出力上は何の意味もありません。
ちょっとややこしい。

追跡ログを埋め込んだサンプルコード

実行してコンソールにどんな順番でログが出力されるか確認してください。

jquery

1//jquery 3.3.1 2var prevState = new $.Deferred().resolve(); 3 4function asyncFuncDef() { 5 var deferred = new $.Deferred(); 6 console.log('asyncFuncDef処理開始'); 7 setTimeout(function() { 8 console.log('setTimeoutの中身開始'); 9 deferred.resolve("timeout"); 10 console.log('setTimeoutの中身終了'); 11 }, 1000); 12 console.log('asyncFuncDef処理終了'); 13 return deferred.promise(); 14} 15 16$(document).on("click",function() { 17 console.log('クリック処理開始'); 18 console.log('prevState.then処理開始'); 19 prevState = prevState.then(function() { 20 console.log("done"); 21 return asyncFuncDef(); 22 }); 23 console.log('prevState.then処理終了'); 24 console.log('クリック処理終了'); 25});

投稿2018/08/28 11:50

編集2018/08/30 11:59
hope_mucci

総合スコア4447

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

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

aaaaaaaa

2018/08/30 09:49

ご回答ありがとうございます。 おかげさまで、だいぶ処理の流れが理解できたかもしれないです。が、一つ質問させてください。 処理の流れは、間違っていなかったら 一回目 1.まず処理の状態を表す変数prevStateをresolveにする。 2.発火条件がクリックであるイベントリスナーが実行されたら、prevStateに 「new $.Deferred().resolve().promise().then...」を代入。 console画面に「done」を表示。返り値であるasyncFuncDef()を実行する。 3.asyncFuncDefの中で新しいDeferredオブジェクトを作る。 settimeoutが第二引数に指定した数値秒後(return後)に実行される。(その間、再びクリックで発火しても待機) 返り値で先ほど新しく作ったDeferredの状態をpendeingにし、prevStateを上書きしてreturn。settimeoutがまだ生きているので実行されるまで待機。 settimeoutで指定経過時間が過ぎたので実行され、resolveとなる。 一回目終わり 二回目以降…一回目と同じ になると思います。 settimeout内のdeferred.resolveでグローバルオブジェクトの一行目で設定した状態(prevState)を更新したということだと思うのですが、 newした時点で別のインスタンスのはずで、returnでpendingにしたり、settimeoutでresolveにしたのは、asyncFuncDef()内のdeferredなのに、なぜprevStateの処理状況を変えることができたのでしょうか。
hope_mucci

2018/08/30 11:53

処理の流れに認識誤りがあります。 1.まず処理の状態を表す変数prevStateをresolveにする。 2.発火条件がクリックであるイベントリスナーが実行されたら,then(...)を非同期で実行するようにスケジュールする。  (ここではまだthenの中の関数は実行されていない) 3.イベントリスナーの関数が終了する。 4.thne(...)の中の関数が実行開始される 5.console.log("done");が実行される 6.asyncFuncDefが実行される 7.asyncFuncDef中、新しいDeferredオブジェクトがNewされる。 8.setTimeoutの中のfunctionが1000ms後実行されるようにスケジュールされる。 9.return deferred.promise();が実行されpromiseオブジェクトが返る。 10.return asyncFuncDef()が解決され、prevStateに9.で返ってきたpromiseオブジェクトが代入される。 11.1000ms後にsetTimeoutの中身が実行開始。 12.deferred.resolveが実行される。deferredの中のpromiseオブジェクト すなわちprevStateの状態がdoneになる。 2回クリックした場合、タイミングとしては10.と11の間にクリックされますが、 10.return asyncFuncDef()が解決され、prevStateに9.で返ってきたpromiseオブジェクトが代入される。 10-1.2回目クリックされ、イベントリスナーの中身が実行開始 10-2.then(...)の中の関数実行が非同期でスケジュールされる。 関数の実行は、prevStateの状態がpendingなのでdoneになるまで待たされる。 10-3.イベントリスナーの関数が終了する。 11.1000ms後にsetTimeoutの中身が実行開始。 12.deferred.resolveが実行される。deferredの中のpromiseオブジェクト すなわちprevStateの状態がdoneになる。 13.thne(...)の中の関数が実行開始される 14.console.log("done");が実行される 以下、1回目の6以下と同じ 2回目クリックの時点ではすでに新しいpromiseオブジェクトがpending状態でprevStateにセットされているので、doneになるまでthenの中の処理を待たせることができるのです。 console.logで1行処理が進むごとに番号か何かを出力するようにすると流れがよくわかります。
aaaaaaaa

2018/09/03 09:54

ご返答ありがとうございました。 間違いを訂正していただいてありがとうございます。詳細な回答で処理の流れがつかめることができました。危うく誤解したまま先に進めるところでした。 てっきり一行目で状態をrezolveにしているのでイベントリスナーが実行された時点で即時にthenの第一引数が実行されるものだとばかり思っておったのですが、 イベントリスナーが終了してから実行されるのですね。これは、thenの効果でそうなったのでしょうか。それともほかの記述が要因となってそうなっているのでしょうか。 また、settimeoutで設定した秒数が0になるまえに再び画面のどこかしらをクリックしたさい、処理の流れの「10.」と「11.」の間に割り込む(?)ことになるそうですが、これは、1秒後に実行するという設定をもったsettimeoutが、割り込んだ処理から見て後ろにいるということ、 つまり「10.」「二度目のクリックの際に実行される処理」「11.」 というキューの形になっているからこのタイミングとなるのでしょうか。
hope_mucci

2018/09/14 16:14

1つ目。 結果的にイベントリスナーの処理が終了してからthenの中身が実行されているだけで、必ずしもイベントリスナーの終了を待つわけではないです。thenとイベントリスナーの関数終了の間に時間のかかる処理があればthenの中の非同期処理のほうが先に動くことも考えられます。 2つ目。 10.までの処理は一瞬で(数msオーダー)終了するので人間のクリック速度ではその間にクリック動作を挟めないというだけです。 promiseの仕組みをこのようなコメント欄でレクチャーするのは大変なので(誤った解説をしないようこちらも非常に気を使います)、あとは自己学習してください。promiseの解説をしているブログやWebサイトはいっぱいあるのでいろんな記事を読んでみてください。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問