JavaScriptライブラリの初期化完了やJSONPのコールバック関数をPromise化する
Google Maps JavaScript APIの初期化完了をPromiseで待機したかったので、方法を考えてみた。
グローバル環境が少し汚れるが、一般にJSONPのコールバックをPromise化できる。
やり方
以下のやり方で、callback
の実行時にコンソールに"SOLVED"
と表示できる。
let globalresolve;
function callback(){
globalresolve();
}
const p1 = new Promise((resolve) => {globalresolve = resolve;});
p1.then(() => {
console.log("SOLVED");
});
- グローバルに
resolve
保存用の変数(例:globalresolve
)を作っておく - Promise生成時に引数の
resolve
をglobalresolve
に代入する - コールバック関数で
globalresolve
を呼び出す
という流れでPromise化できる。
使用例1
Google Maps JavaScript APIの読み込み完了時に関数initMap
を呼ぶようにする場合。
let mapresolve;
function initMap() {
mapresolve();
}
(new Promise((resolve) => {mapresolve = resolve;})).then(() => {
console.log("SOLVED")}
);
<script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"></script>
使用例2
単体でPromise化してもあまり恩恵は大きくないのだが、複数のライブラリを使用していてすべてのライブラリの読み込み完了後に何かしら処理をしたい、
といった場合にPromise.all
を使って並列して読み込みつつ、すべてが完了するのを待機してから次の処理に移る、といったことが可能。
let gr1, gr2;
function callback1(){
gr1();
}
function callback2(){
gr2();
}
const p1 = new Promise((r) => {gr1 = r;});
const p2 = new Promise((r) => {gr2 = r;});
Promise.all([p1, p2]).then(() => {
console.log("ALL DONE");
});
できる限りグローバルを汚したくない場合
一応、細かくスコープを区切ればグローバルに保管用変数を追加しなくてもPromise化可能(callback
の巻き上げが起こらないので、callback = () => outerresolve();
が実行される前にコールバック関数が呼び出されてしまうとNGな点は注意)。
let callback;
{
let outerresolve;
callback = () => outerresolve();
(new Promise((resolve) => {outerresolve = resolve;})).then(() => {
console.log("SOLVED")}
);
}
参考
- より一般にJSONPをPromiseにする場合: 【Promise】JSONPの読み込みをThenableにする - Qiita