Chrome拡張機能の非同期APIはコールバックにより実装されています. 例えば, 拡張機能ごとに用意されるストレージからデータを取得する場合, ストレージへのアクセス中にJavaScriptの処理が中断されるのを防ぐため, 非同期APIが用意されています.
// background.js chrome.storage.local.get(['admin'], (result1) => { chrome.storage.local.get([`user/${result1.admin}/name`], (result2) => { console.log(result2) }) })
このコールバックによる非同期APIはJavaScriptにおいては一般的な非同期APIの実装手法ですが, 「コールバック地獄」という問題を引き起こします. これを解決する手法としてES2015で追加されたPromise
とジェネレータ, もしくはES2017で追加されたasync
/await
を利用する手法が知られています.
しかしChrome拡張機能の非同期APIはコールバックにより実装されているため, Chrome拡張機能の中では直接的にはPromise
やasync
/await
を利用することが出来ません. そのため, 手動で非同期APIをPromiseでWrapするか, 以下のようなWrapperライブラリを利用する必要があります.
- tfoxy/chrome-promise
- Chrome Extension APIのコールバックによる非同期APIをPromiseで提供するWrapperライブラリ
- mozilla/webextension-polyfill
- Chrome向けのWeb Extension APIのPolyfill
この記事ではmozilla/webextension-polyfillを使ってコールバック地獄を解決する方法を紹介します.
mozilla/webextension-polyfillを使ってコールバック地獄を解決する
ブラウザ間で統一的なAPIで拡張機能を作成できるよう策定されたWeb Extension APIというものがあり, 現時点でMozilla FirefoxとMicrosoft Edgeでサポートされています. mozilla/webextension-polyfillはこのWeb Extension APIのChrome向けのPolyfillとなっています. Web Extension APIの非同期APIは全てPromiseで提供されているため, Chrome拡張機能でコールバック地獄を解決する方法として用いることができます.
mozilla/webextension-polyfillはnpm.comからもインストールできますが, モジュールバンドラなどを挟む必要があるため, 少々手間です. これに変わるより手軽なものとして, unpkg.comから直接ライブラリをダウンロードし, manifest.json
のbackground.scripts
などから読み込む方法があります.
$ cd extension-project $ wget https://unpkg.com/webextension-polyfill/dist/browser-polyfill.min.js
// manifest.json { // ... "background": { "scripts": [ "browser-polyfill.min.js", "background.js" ] }, "content_scripts": [{ // ... "js": [ "browser-polyfill.min.js", "content.js" ] }] }
このようにPolyfillを読み込むことでbackground.js
などからbrowser
という名前空間からWeb Extension APIにアクセスできます.
// background.js browser.storage.local.get(['admin']) .then((result) => { return browser.storage.local.get([`user/${result.admin}/name`]) }) .then((result) => { console.log(result) })
非同期APIはPromise
を返すので, 次のようにasync
/await
を利用したコードにも書き換えることもできます.
// background.js (async () => { const result1 = await browser.storage.local.get(['admin']) const result2 = await browser.storage.local.get([`user/${result1.admin}/name`]) console.log(result2) })()
以上, Chrome拡張機能でコールバック地獄を回避するTIPSでした.