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

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

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

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

Q&A

解決済

2回答

980閲覧

sleepを条件付きで解除したい

ponzu1990

総合スコア57

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

1グッド

0クリップ

投稿2017/11/21 09:44

ぐぐっても見かけなかっので質問させてください。

ある一定の条件を満たすとsleepから抜けるものを考えているのですが、
本当にこれでいいのか疑問を持っています。

node.js

1flag = 0; //global - 他の部分で状態が変化する 2s = 30; 3var e = new Date().getTime() + (s * 1000); 4while(new Date().getTime() <= e) { 5 if(flag !=0) break; 6}

このコードは30秒間sleep(CPU燃焼?)し、
sleep中であってもflagの値が変化したらsleepをやめるものです。
setTimeout()だと状態変化に対応できないので…。

使用するnode.jsのversion、及びnpmパッケージは問いません。

もし良い方法がありましたら、教えていただけますと大変ありがたいです。

KSwordOfHaste👍を押しています

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

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

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

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

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

guest

回答2

0

ベストアンサー

この質問文は「スリープを実装したいから」頑張って作ったのでしょうか?
それともビジーループを実装したいから実験したいのでしょうか?
後者が目的ならアドバイスは役に立たないので読み飛ばしてください。

もし前者なら、下記のアドバイスが役に立つかもしれません。


JavaScriptやNode.jsにはsleepというコマンドはありません。
何故ならシングルスレッドだから、sleepしている間全ての作業がストップしてしまうからです。

スタートがブラウザなので、ブラウザから要素をクリックしたぞとか、要素の上をマウスカーソルが通過したぞみたいなイベントの山をバンバン処理しなきゃいけないですからね。
その代わりにイベントが来るまで怠惰に休むという仕組みを持っています。

JavaScriptの世界では、
Sleepの代わりにやりたいことを予め関数として用意しておいて、
setTimeoutという関数と同時に利用します。

JavaScript

1flag = 0; // global - 他の部分で状態が変化する 2var s = 30; 3var fn = function() { 4 if (flag != 0) return; 5 // dosomething 6} 7setTimeout(fn, s * 1000);

30秒後に起動した際、「ありゃ、フラグが0から変更されてるじゃん…起動やめよっと」と
判断して内部の処理を取りやめるフローに変更しています。


【ここから追記】

sleep中であってもflagの値が変化したらsleepをやめるものです。

つまり、スリープをやめて今すぐ発火して欲しいのですね。
私の上の回答文ではやりたい事は満たせないですね……
そのように作り変えましょう!

setTimeoutの戻り値のIDを利用して、下記の様に登録したsetTimeoutを取り消す事も可能です。
つまり、登録はしておいたけどやっぱりキャンセルは可能なんですね。
後で使うので覚えておいてください。

JavaScript

1var fnId = setTimeout(fn, s * 1000); 2clearTimeout(fnId);

そしてMobXというパッケージを導入しましょう。
参考サイト: これからMobXをはじめる人へ

MobXのパッケージはReactという動的なページを表示するフレームワークのおまけみたいな扱いをされますが、本質は変数のオブザーバにあります。
オブザーバというのは変数を常に監視して、値が変わったら即座に関数を実行するデザインパターンを指します。

30秒後に登録しておいたsetTimeoutの関数を取り下げつつ、今すぐ実行します。
因みにMobXに限らず自分で作ったイベントエミッター系は同期実行なのでsetTimeoutに割り込まれる事はなく、
30秒と同時にフラグが経って2回関数が叩かれるという残念な事は起こりません。

以前私がイベントループを上手く回す為にベンチマークと称して使ってたコードがありますので、
流用してサクッと実装してみましょう。

JavaScript

1const {observable, autorun, reaction} = require('mobx'); 2global.state = { 3 id: null 4}; 5global.store = observable({ 6 flag: 0 // globalのflag変数はこっちに引っ越しました。少し使い勝手が変わるので注意 7}); 8 9const fn = function() { 10 if (state.id == null) return; 11 state.id = null; 12 // do something 13} 14state.id = setTimeout(fn, 31000); 15reaction( 16 () => store.flag, 17 () => { 18 if (store.flag === 0) return; 19 if (state.id) clearTimeout(state.id); // 先程実行したものを取り下げて 20 fn(); // 手動実行! 21 } 22)

ざっとこんな感じですかね。

投稿2017/11/21 11:11

編集2017/11/21 12:00
miyabi-sun

総合スコア21158

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

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

miyabi-sun

2017/11/21 11:20

実は私の解答はKSwordOfHasteさんとつながっています。 予め関数を作って置かなければならないので、 JSやNode.jsは非同期処理が増えると所謂コールバック地獄というネストだらけになってしまいます。 そこでPromiseやAsync/Awaitという仕組みが用意されており、 簡潔に非同期処理を順に実行していく流れをスマートに書けるようになっています。 やりたい事が高度になってソースコードがぐちゃぐちゃになったら検討してみてください。
miyabi-sun

2017/11/21 12:01 編集

あ、そうか…きっとこれはフラグがONになったら発火してほしいんですね。 回答文を見直して修正かけておきます。 -> 修正しました。これで実現出来るはずです!
KSwordOfHaste

2017/11/21 12:05

Reactの何たるかを分かっていないのですがobserverという単語から「ポーリングでなくイベントで」というところをmiyabi-sunさんは示唆しておられるのだろうかと想像しました。 質問者さんがどのような状況なのか興味がわきます・・・
miyabi-sun

2017/11/21 12:19 編集

そうですねえ、そもそも目的が何個かの処理を並列で走らせて、 全部終わったら完了処理を走らせたいだけかもしれないですからね。 その場合ふたりともはずれで、Promiseで書けばいいじゃんって話ですよね。
KSwordOfHaste

2017/11/21 12:48

先日Promise.raceって何に使うんだろうと不思議に感じたのですが、ひょっとして「30秒timeoutか、目的タスクが終わるか」どちらか一方を待つようなときにraceの出番なのかと今気づきました。質問者さんの状況がもしそうならポーリングでなくてPromiseでも書けそうな気がします。
ponzu1990

2017/11/22 08:52 編集

300秒の間にユーザーに[yes]/[no]の選択をしてもらい、タイムアウトまたは[no]だったら後続処理に行く…ということがしたかったのです。初めてPromise.raceを今はじめて知って見てみましてやりたいことがかなりできそうなのですが、ユーザーの応答をsocket.onで受け取る仕組みのため、raceで実装するのは難しいと思いました…。あとmiyabi-sunさんがご教示してくださったMobXが今回の件でもかなり使えるので活用していきたいと思います。みなさま本当に有難うございます。
KSwordOfHaste

2017/11/22 07:12 編集

後から発生する割り込み的な機能の実装なのですね! こうしたもの(単純な非同期の連鎖でないもの)はPromiseを使う制御よりかえってmobxのようなobservableによる実装の方が直感的で分かり易い気がしました。 勉強になりました。>miyabi-sunさん 最初質問から受けた印象からは想像できなかった点で勉強させていただき感謝しています。質問に「後から発生する事象により非同期処理の制御を変更」みたいな表現があると察しの悪い自分でもポイントがつかみやすかったとも感じましたw;>質問者さん。
KSwordOfHaste

2017/11/22 07:14

はずかしながら、自分にとってmiyabi-sunさんの回答の方が有意義な印象ですw;
miyabi-sun

2017/11/22 07:28

いえいえ、Promise.raceという新しい発見もありましたし、 殆どのエンジニアはKSwordOfHasteさんのようにPromise主体で考えるでしょう。 なので、もし後続の実装で詰まった時は解答が得られやすいんですよね。 既存の質問文のwhileを残した辺りも質問者寄りの印象としては良かったと思います。 安心感が違いますね。 私のはイベントループ使えという方向に持っていく為にポーリング全否定してますから、フォローするために長文になってしまいました。 私が蛇の道は蛇といった感じで、 たまたま使ってたMobXライブラリが今回刺さっただけという印象ありますね。 今後の事を考えたらどうだか分からんってのが感想です(/ω\)
ponzu1990

2017/11/24 01:05

おふた方の情報が有益すぎて、どちらにもベストアンサーをつけたかったのですが…知りたいこと以上のことを知ることができたmiyabi-sunさんの回答につけさせていただきました。KSwordOfHasteさん、その名の通り素早く的確なレスありがとうございました。
KSwordOfHaste

2017/11/24 01:09

BAはしかるべき回答につけていただくのが一番だと思います! 自分もとても勉強になり感謝しているのです。
guest

0

これはビジーループと呼ばれるものですね。ぐぐっても見つからないのは「ビジーループは特別な理由がない限り普通はしないもの」だからだと思います。シングルスレッドのECMAScriptではなおのことではないでしょうか?

どのような状況かよくわかりませんが、例えばsetTimerにより短い時間間隔ごとに状態変化をチェックするようにしてはいかがでしょうか?

また、もし非同期処理(setTimerなど)を上から下へ同期的に実行されるかのように書きたいならPromise/async/awaitを使うのも一つの方法と思います。

javascript

1function promissSleep(t) { 2 return new Promise(resolve => { 3 setTimeout(resolve, t); 4 }); 5} 6 7var flag = 0; 8 9// async関数の中では非同期処理を同期的に書ける 10async function waitForFlag(s) { 11 var e = new Date().getTime() + (s * 1000); 12 while (new Date().getTime() <= e) { 13 await promissSleep(100); // 10msずつsleep=>あまりに頻繁すぎるので100msに... 14 if (flag !== 0) break; 15 } 16} 17 18// 呼び出し元もasync関数にする。 19// この関数の呼び元もasync... 20async function foo() { 21 await waitForFlag(30); 22 ... 23}

ご参考:
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function

投稿2017/11/21 10:49

編集2017/11/21 11:45
KSwordOfHaste

総合スコア18394

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

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

KSwordOfHaste

2017/11/21 11:05

promissSleepで無条件に1秒まつようになってたのでコードを訂正させていただきました。失礼しました。
ponzu1990

2017/11/21 11:19

早くて的確な回答をありがとうございます。これなら無駄な負荷をかけずにやりたいことができそうなので使用させていただきます。ありがとうございます<(╹ヮ╹)>
miyabi-sun

2017/11/21 11:33

ん〜…10msは明らかに連打しすぎで、 whileのループより多少マシ程度でイベントループも汚れますし負荷もあまり減ってないと思われます。
KSwordOfHaste

2017/11/21 11:43

たしかに・・・普通のWindowsとかですとほぼ最小のインターバルということになりそうです。せめて100msぐらいにしておけば多少行儀の悪さは軽減されるでしょうか・・・
miyabi-sun

2017/11/21 11:47

そうですね、対応ありがとうございます!
KSwordOfHaste

2017/11/21 11:49

ご指摘ありがとうございました。
ponzu1990

2017/11/22 00:46

ありがとうございます、私も100msで運用したいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問