前提・実現したいこと
複数の関数を時間差で実行してそれを繰り返す方法を知りたく思っています。
#####実行内容
console.logで下記を表示
(1秒後)コーヒーを注文する→(2秒後)支払いをする→(5秒後)飲む→(1秒後)コーヒーを注文する
→(2秒後)支払いをする→(5秒後)飲む・・・エンドレスにこの処理
※本当はもっと色々な処理をさせたいのですが質問しやすいようにシンプルにしました。
検索しているとこのような処理に「Promise」を使用することをすすめている記事が多く、ひとまず自分なりに記述をしてみました。
ただ、これを繰り返し実行する方法がわからずにいます。
###知りたいこと
- 表題の「時間差で実行、さらに繰り返し」処理を行うにはどのような記述を行うのが良いのか
Promise
で処理をする選択は良いのか?その場合繰り返しをさせるにはどのように記述するべきか- そもそも私の
Promise
の記述に誤りが無いか、もしくはもっと良い記述があればご指摘いただけると助かります
JavaScript
1function orderCoffee() { 2 return new Promise((resolve, reject) => { 3 setTimeout(() => { 4 console.log('コーヒーを注文する'); 5 resolve(); 6 }, 1000); 7 }); 8} 9function payBill() { 10 return new Promise((resolve, reject) => { 11 setTimeout(() => { 12 console.log('支払いをする'); 13 resolve(); 14 }, 2000); 15 }); 16} 17function drinkCoffee() { 18 return new Promise((resolve, reject) => { 19 setTimeout(() => { 20 console.log('飲む'); 21 resolve(); 22 }, 5000); 23 }); 24} 25 26//下記を繰り返し実行させたい 27orderCoffee() 28 .then(payBill) 29 .then(drinkCoffee)
どうぞ宜しくお願いします
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

回答3件
0
ベストアンサー
Q1. 「時間差で実行、さらに繰り返し」処理を行うには?
とりあえず、Promise
を使う前提で書いて見たコードが以下となります。
Pattern A
const asyncOrderDrink = () => { return new Promise(resolve => { setTimeout(() => { console.log('コーヒーを注文する') resolve(); }, 1000) }); } const asyncPayBill = () => { return new Promise(resolve => { setTimeout(() => { console.log('料金を支払う') resolve(); }, 2000) }); } const asyncDrinkCoffee = () => { return new Promise(resolve => { setTimeout(() => { console.log('コーヒーを飲む') resolve(); }, 3000) }); } function* makeLoopGenerator (fns) { while(true) { for (var fn of fns) { yield fn(); } } } function runLoop(generator) { function loop() { generator.next().value.then(loop); } loop(); } runLoop( makeLoopGenerator([asyncOrderDrink, asyncPayBill, asyncDrinkCoffee]) );
Pattern B(追記)
質問者さんの前回の質問を見る限りでは、単に時間差で複数の同期処理を実行させたいように見受けられるので、maisumakun
さんの回答にあるようにsleep
関数のようなものを作って、各同期処理の前で実行してあげるのが良さそうです。
ということで、maisumakun
さんの回答を参考に以下、generator
版のコードを追加。
const sleep = (delay) => { return new Promise((resolve) => { setTimeout(resolve, delay) }); } const orderCoffee = (v) => console.log(v); const payBill = (v) => console.log(v); const drinkCoffee = (v) => console.log(v); function runLoop(gen) { function loop() { gen.next().value.then(loop); } loop(); } function* makeLoopGenerator() { while(true) { yield sleep(1000); orderCoffee('コーヒーを注文する'); yield sleep(2000); payBill('料金を支払う'); yield sleep(3000); drinkCoffee('コーヒーを飲む'); } } runLoop(makeLoopGenerator())
※ 追記のコードも最初のコードのように、ループ内で実行させたい関数が可変の場合でも対応できるようにしたかったですが、時間切れの為、非対応としました。
Q2. Promiseで処理をする選択は良いのか?
質問者さんの前回の質問の場合は、「一定間隔」で複数の(異なる)処理を実行させたいというものだったので、Promise
を使わずに単に、setTimeout()
関数で定期実行させてあげるだけで良いかと思います。
以下、前回の質問の「異なる複数の処理」に対応したコードです。
以下を1000秒毎に実行
- 背景色を変える関数を実行
- 背景色を変える関数を実行
- テキストを変える関数を実行
- 背景色を変える関数を実行
- 背景色を変える関数を実行
- テキストを変える関数を実行
- 上記の繰り返し
html
<div id="containerA" class="spring"></div> <div id="containerB">-----</div>
CSS
#containerA { width: 100px; height: 100px; } #containerB { width: 100px; background: gray; font-size: 20px; text-align: center; } .spring { background-color: pink; } .summer { background-color: red; } .autumn { background-color: brown; } .winter { background-color: blue; }
JavaScript
(function(global, document) { function makeThunk(func, ...rest) { return function() { return func(...rest); } } const app = (function() { function changeClassName(className, element) { element.className = className; } function changeText(value, element) { element.textContent = value; } function* makeLoopGenerator(...funcs) { while(true) { for (let func of funcs) { yield func(); } } } function runLoop(generator) { generator.next(); setTimeout(runLoop, 1000, generator); } function init() { const containerA = document.getElementById('containerA'); const containerB = document.getElementById('containerB'); const funcs = [ makeThunk(changeClassName, 'spring', containerA), makeThunk(changeClassName, 'summer', containerA), makeThunk(changeText, 'HELLO', containerB), makeThunk(changeClassName, 'autumn', containerA), makeThunk(changeClassName, 'winter', containerA), makeThunk(changeText, 'WORLD', containerB) ]; runLoop(makeLoopGenerator(...funcs)); } return { init: init } })(); document.addEventListener('DOMContentLoaded', app.init); })(window, document);
Q3. そもそも私のPromiseの記述に誤りが無いか、もしくはもっと良い記述があればご指摘いただけると助かります
回答に載せたコードでもエラー対応をするためのコードは省略していますが、エラーが発生する可能性があるのであれば、new Promise()
に渡すコールバック関数内で、非同期処理が失敗した時にreject()
が実行されるようにコードを書いてあげる必要があります。
参考
掲載コードの中でgenerator
、spread syntax
、rest parameters
を使っていますが、詳細やブラウザ対応状況は以下でご確認ください。(英語記事へのリンクになっていますが、リンク先ページで言語切り替えが可能です。)
投稿2018/05/16 23:57
編集2018/05/17 10:04総合スコア2415
0
async
-await
を使えば、「非同期な無限ループ」も、ものすごくすっきり書けます。
javascript
1function sleep(time){ 2 return new Promise(resolve => { 3 setTimeout(resolve, time); 4 }); 5} 6 7async function main(){ 8 for(;;) { 9 await sleep(1000); 10 console.log('コーヒーを注文する'); 11 await sleep(2000); 12 console.log('支払いをする'); 13 await sleep(5000); 14 console.log('飲む'); 15 } 16} 17 18main();
なお、IE(11でも非対応)やiOS 10.3未満では非対応なので(Can I use)、そういう環境ではBabelなどで変換することが必要です。
Promise
で処理をする選択は良いのか?その場合繰り返しをさせるにはどのように記述するべきか
Promise
は単なるライブラリの域を超えて、ジェネレーターやasync
-await
といった文法構造とも結びついている(上のコードでもnew Promise
の結果をawait
で「待たせて」います)、fetch
がPromise
で結果を返すなど、JavaScriptの基盤に組み込まれつつある機能です。
IE 11が未対応という問題がありますが、Promise
だけであれば比較的容易にPolyfillできますし、「Promise
にして書きやすい」のであれば積極的に使っていっていいのではないかと思います。
投稿2018/05/17 00:24
編集2018/05/17 04:22総合スコア146544
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

0
未検証ですが、参考までに。
JavaScript
1setTimeout(function handleTimeout (index, data) { 2 console.log(data[index][0]); 3 index = ++index % data.length; 4 setTimeout(handleTimeout, data[index][1], index, data); 5}, 1000, 0, [['コーヒーを注文する', 1000], ['支払いをする', 2000], ['飲む', 5000]]);
検索しているとこのような処理に「Promise」を使用することをすすめている記事が多く、
お勧めされる理由は読みましたか。
現在の要件を踏まえて、他の方法とPromiseを比較検討した上でPromiseが最良と判断していますか。
少なくとも、「新しい」という理由だけで Promise
を選択するのは間違っていると私は思います。
私はこの条件であれば、再帰関数を選択します。
Re: umauman さん
投稿2018/05/17 03:53
総合スコア18194
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/05/17 05:58
2018/05/17 06:23
2018/05/17 13:59