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

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

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

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

Q&A

解決済

1回答

1722閲覧

逐次的かつ非同期なsleep

mightyMask

総合スコア143

JavaScript

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

0グッド

3クリップ

投稿2019/08/11 16:00

編集2019/08/12 12:21

#質問
JavaScriptで、逐次的・非同期なsleep(またはwait)っぽいプロシージャは実現できないでしょうか。

#期待する動作

JavaScript

1function func() { 2 console.log("2"); 3 sleep(3000); 4 console.log("3"); 5}; 6 7console.log("1"); 8func(); 9console.log("4");

のように記述すると、

(起動直後...)

1 2

(3秒後...)

3 4

のように出力されるように動作し、かつ3秒の待ち時間の間はビジー状態にならないようなsleepプロシージャを実現したいです。

#問題点
逐次的なsleepを愚直に実装すると、スクリプトから処理が戻らずビジー状態が継続され、画面が固まってしまうようです。

非同期なsleepを愚直に実装すると、setTimeoutの後の処理が先に行われてしまいます。

解決するための手段として、promiseと言う機能が用意されているようですが、コールスタックが呼び出し元に返った時、その先の処理は先に行われてしまうようです。
promiseの挙動は把握仕切れていませんが、おそらくこれを解決するのは無理っぽいように感じました。

呼び出し元で後に行いたい処理を呼び出し先に移すとか、コールバック関数を渡すとかで無理矢理実現することもできるのですが、(Javaで言う処の)publicを増やさないといけないとか、引数を増やさないといけないとかで、カプセル化や疎結合性が壊れてしまいます。

#解決へのアイディア
sleepが呼ばれた時コールスタックを保存し、一旦ビジー状態を解除。その後timeoutプロシージャが呼ばれたらスタックの状態を復元し、そこから処理を再開。
つまり、コールスタックの状態を保存・復元できるような機能があればこれは解決できるように思いますが、こういった処理は用意されているのでしょうか。他の実現方法でも構いませんが。

#開発環境
Nim(Compiler Version 0.20.0 [MacOSX: amd64])と言うコンパイル言語で、中間コードとしてJavaScriptソースを生成しています。
この言語にはdomを操作する標準ライブラリが用意されていて、
ほとんど任意の処理に対して、JavaScriptで実現可能 ⇔ Nimで実現可能
と言う感じになっています。
Nimはマイナーな言語ですので、このようなJavaScriptメインの質問形式にさせていただきました。
Nimの方は全く知らなくても問題ないです。

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

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

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

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

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

guest

回答1

0

ベストアンサー

非ビジーという前提がよくわかりませんでしたが、これではだめなんでしょうか。

const sleep = async ms => { return new Promise(resolve => { setTimeout(resolve, ms); }); }; const main = async () => { console.log("start"); await sleep(3000); console.log("3秒経過"); }; main();

投稿2019/08/12 00:39

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

mightyMask

2019/08/12 02:17

const sleep = async ms => {   return new Promise(resolve => {     setTimeout(resolve, ms);   }); }; const main = async () => {   console.log("2");   await sleep(3000);   console.log("3"); }; console.log("1"); main(); console.log("4"); とすると、 (起動直後...) 1 2 4 (3秒後...) 3 となってしまうようです。
退会済みユーザー

退会済みユーザー

2019/08/12 05:36

そりゃそうですね。 例でいうと処理はすべてmainの中に入れてあげないといけません。 async関数の中でないとawaitは使えないので。
退会済みユーザー

退会済みユーザー

2019/08/12 05:37

期待すべき動作を考えると console.log("1"); await main(); console.log("4"); としたいところですが、現行仕様ではトップレベルawaitはできないようになっています。
退会済みユーザー

退会済みユーザー

2019/08/12 05:39

とりあえずawaitで同期的に動作するという点を覚えてください。
think49

2019/08/12 05:46

細かい指摘ですが、asyncは「非同期」ですね。 そもそも、「同期的 = ビジー状態」なので、要件が矛盾しているのですが…。
mightyMask

2019/08/12 06:01

とりあえずトップレベルでさえ使わなければ問題なかったので、解決しました と言う旨のコメントを残してからBAにしたつもりでしたが反映されていなかったようです。 >think49 さん 同期的とは、前の処理が終わってから次の処理を開始する所謂逐次的な処理のこと。 ビジー状態とは、スクリプトが処理をしていてブラウザの描画が止まっている状態。 このような解釈をしていたのですが、間違っていますでしょうか? 問題があるようでしたら質問文を編集します。 (既に解決済みですが、後に検索とかでここにたどり着いた人のため)
think49

2019/08/12 06:15

To: mightyMask さん 「ユーザ操作」と「JavaScriptコード操作」が「同期的」に動作している場合、同期的故に「JavaScriptコード操作」の完了を待たなければ、「ユーザ操作」に移ることが出来ません。この待機中が「ビジー状態」と呼ばれます。 ビジー状態を回避するには「ユーザ操作」と「JavaScriptコード操作」を独立して動かせるように「非同期」にする必要があります。 質問文を訂正するのであれば、「非同期コードの実行完了時に直後のコードが実行されるようにする方法を教えて下さい」というところかと思います。 「期待する挙動】を具体的に書けば、間違いはないと思います。
think49

2019/08/12 06:18

あと、コードで書くと確実に伝わりますね。 「下記コードで 1 -> 2 の順に実行される方法を教えて下さい」 setTimeout(() => console.log(1), 100); // 1 console.log(2); // 2
退会済みユーザー

退会済みユーザー

2019/08/13 04:21

逐次的って同期的ということですよね。 同期的かつ非同期というのはおかしいのでは・・・? 素直に「同期的にsleepを実行したい」で良いと思います。
mightyMask

2019/08/13 08:03

「同期的と言うのは逐次的な処理のことではない」と言う事をthink49さんの指摘で気づいて訂正したつもりです。 逐次的と同期的が同じ様にに感じているのなら以前の私と同じ勘違いをしてるんじゃないかなと思います。
think49

2019/08/13 09:11

Ro: dyoshikawa さん 下記に「非同期の逐次処理」の実装例があります。 http://azu.github.io/promises-book/#promise-sequence 「ユーザ操作→逐次処理」の間は「非同期」に出来ます。 To: mightyMask さん 修正ありがとうございます。
退会済みユーザー

退会済みユーザー

2019/08/13 09:53

あ、そうなんですね。 私の勘違いということで失礼しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問