-ao- ramune blog

©2024 unio / GO2直営からふるラムネ
2019年10月01日

JavaScriptでforループ中にsleepしたい

setTimeout

JavaScriptではsetTimeoutを使ってsleepします。

                
                    const start = Date.now();

                    // 500ミリ秒sleep
                    window.setTimeout(() => {
                        console.log("run: " + (Date.now() - start) + " msec");
                    }, 500);
                
            
                
                    > run: 501 msec
                
            

このsetTimeoutを使って、ループ間のsleepを設定します。

                
                    const start = Date.now();

                    // 500ミリ秒毎にループを実行したいが...
                    for (let i = 0; i < 5; i++) {
                        window.setTimeout(() => {
                            console.log("run: " + (Date.now() - start) + " msec");
                        }, 500);
                    }
                
            

パット見よさそうですが、これでは「500ミリ秒後に関数を実行 x 5回」となってしまいます。

                
                    // setTimeoutで指定した関数が、500ミリ秒後にすべて呼び出される
                    > run: 500 msec
                    > run: 501 msec
                    > run: 501 msec
                    > run: 501 msec
                    > run: 501 msec
                
            

このように、ループの中で単純にsetTimeoutを呼び出しても期待する動作になりません。

ループ毎にsleep

ループ終了時に次のループ処理をsetTimeoutすることで、ループ毎にsleepする動作を実現できます。

                
                    /**
                     * @param func : (val: any) => any 1ループの処理関数
                     * @param count: number            ループ回数
                     * @param sleep: number            sleepミリ秒
                     * @param val  : any = {}          処理関数に渡す引数
                     */
                    function intervalLoop(func, count, sleep, val = {}) {
                        window.setTimeout(() => {
                            const result = func(val);
                            if (count > 1) {
                                intervalLoop(func, --count, sleep, result);
                            }
                        }, sleep);
                    }
                
            
TypeScript版
                
                    function intervalLoop(func: (val: any) => any, count: number, sleep: number, val: any = {}) {
                        window.setTimeout(() => {
                            const result = func(val);
                            if (count > 1) {
                                intervalLoop(func, --count, sleep, result);
                            }
                        }, sleep);
                    }
                
            

ループ結果をフィードバックできるように、処理関数の結果を引数で受け取れるようにしています。

                
                    intervalLoop(start => {
                        console.log("run: " + (Date.now() - start) + " msec");
                        return start;
                    }, 5, 500, Date.now());
                
            
                
                    // 500ミリ秒後に次のループが動作している
                    > run: 501 msec
                    > run: 1003 msec
                    > run: 1503 msec
                    > run: 2007 msec
                    > run: 2509 msec