ぐぐっても見かけなかっので質問させてください。
ある一定の条件を満たすと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パッケージは問いません。
もし良い方法がありましたら、教えていただけますと大変ありがたいです。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答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総合スコア21158
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総合スコア18394
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/11/21 11:19
2017/11/21 11:33
2017/11/21 11:43
2017/11/21 11:47
2017/11/21 11:49
2017/11/22 00:46
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/11/21 11:20
2017/11/21 12:01 編集
2017/11/21 12:05
2017/11/21 12:19 編集
2017/11/21 12:48
2017/11/22 08:52 編集
2017/11/22 07:12 編集
2017/11/22 07:14
2017/11/22 07:28
2017/11/24 01:05
2017/11/24 01:09