JavaScript の Promise は非同期処理の結果を返すオブジェクトです。これを用いることで計算資源を遊ばせることなく処理を続行できます。API を叩いた結果など、いくつかの非同期処理はまずこの Promise オブジェクトが返ってくるようになっています。しかしながら、Promise が返って来ないが Promise 時に判定を行いたい時があります。例えば、あるライブラリの中に非同期で DOM を書き変える処理が入っているが、その非同期処理の Promise を返さないときです。こういった際は DOM が書き変わったことを確認してから処理を続行したくなる時があります。この手の判断を Promise で扱えると処理を書きやすくなります。これは次でできます。
/**
* 条件が整うまで待つ Promise を返します。
* @param {() => boolean} conditionCallback resolve が走るための真偽値を返す関数
* @param {Number} intervalMillSecond ポーリング間隔ミリ秒
* @param {Number} timeoutMillSecond タイムアウトミリ秒。 0以下を渡すとタイムアウトなし
* @return Promise
*/
function waitAsync(conditionCallback, intervalMillSecond = 10, timeoutMillSecond = 0){
// 条件が成立するまで setInterval でポーリング的なループ
return new Promise(function(resolve, reject){
let loopCount = 0;
const intervalId = setInterval(function(){
if(timeoutMillSecond > 0 && loopCount * intervalMillSecond > timeoutMillSecond){
reject('timeout');// 条件が満たされないままタイムアウトを迎えたことを示す
}
if(!conditionCallback()){
// 条件関数が falsy を返した時はループ続行
return;
}
// 条件関数が truthy を返した時はループ用の interval を消去
clearInterval(intervalId);
// 条件関数が true を返した時は resolve 関数を実行して条件が満たされたことを示す
resolve('success');
}, intervalMillSecond)
});
}
// 使用例
const videoEl = document.querySelector('video');
document.querySelector('video').currentTime += 2; // 動画の現在時刻を 2 秒進める
waitAsync(() => !videoEl.seeking, 1, 1000)// 1ms 間隔で動画がシーキング中かチェック。1秒待ったらタイムアウト
.then(() => videoEl.currentTime += 2);// 動画が 2 秒後に動き終わったら更に 2 秒進める
上記コメントの様にポーリングし、ポーリング中の状態で結果を解決する Promise を作ることで目的を果たせます。余談ですが setInterval によるポーリングの代わりに while ループを用いると while ループ内の処理の実行が画面のレンダリングより優先されるのか web ページがフリーズします。