#Chrome ググっても『[[Chrome Extension]]を無効にしよう!』みたいな開発とは無関係のモノばかり表示されるのでまとめる。 ## 事象 以下のコードを`background.service_worker`のスクリプトに記載して、`content_scripts.js`のスクリプトから呼び出すとエラーになる。 ```js:background.js chrome.runtime.onMessage.addListener(async function ( request, sender, sendResponse ) { if (request.action === "translate") { const translated = await translate(request.payload.text); sendResponse(translated); } }); ``` エラー。 ``` Unchecked runtime.lastError: The message port closed before a response was received. ``` ## 原因 2つある。 ### イベントハンドラで`true`をreturnしていなかった [Message passing \- Chrome Developers](https://developer.chrome.com/docs/extensions/mv3/messaging/#simple) に以下の記述があった。 > Note: The sendResponse callback is only valid if used synchronously, or if the event handler returns true to indicate that it will respond asynchronously. The sendMessage function's callback will be invoked automatically if no handlers return true or if the sendResponse callback is garbage-collected. `sendRespons`コールバックを使うには以下いずれかを満たす必要があるらしい。 - イベントハンドラの中身に非同期処理がない - イベントハンドラの中身に非同期処理はあるが、`true`をreturnしている `true`をreturnすることによって、メッセージ送信元に『これから`sendResponse`が非同期で送られてくるからヨロシク!』と意思疎通をしているらしい。だから、メッセージ送信元は非同期の`sendResponse`が来るのを待っていられる。もし何もreturnしないと、『もう`sendResponse`は来ないんだな..じゃあいっか』ということで[[Gorbage Collector]]によって遮断されるよう。 [[MDN]]の[[runtime.onMessage]]にも以下の記載がある。 > 非同期的に返信するには、二つの方法があります。 > > - イベントリスナーから true を返す。こうすることで、リスナーから復帰した後でも sendResponse 関数が有効なままになるため、後で実行することができます。例を参照してください。 > - イベントリスナーから Promise を返して、返信が準備できた後にそれを解決する (またはエラーの場合は拒否する)。例を参照してください。 2つ目の[[Promise]]を返す方法を推奨としているが、2022-01-30時点では[[Firefox]]以外のブラウザが未対応なため使えない。もし、[[Google Chrome]]が対応したら以下のようにも書けるはず。 ```js chrome.runtime.onMessage.addListener((request, sender) => { if (request.action === "translate") { return translate(request.payload.text); } }); ``` ### [[Async Function]]を使っていた こちらも [runtime\.onMessage \- Mozilla \| MDN](https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage#%E5%BC%95%E6%95%B0) に注意書きがあった。 > addListener を次のような async 関数を使って実行しないでください。 詳細は原文を。 ## 解決方法 - [[Async Function]]を使わず[[Promise.then]]を使う - 最後に`return true`する ```js:background.js chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { if (request.action === "translate") { translate(request.payload.text).then((translated) => { sendResponse(translated); }); } return true; }); ``` ## 参考 - [chrome\.runtime\.onMessageでreturn trueしたけどundefinedが返ってきた \- Qiita](https://qiita.com/noenture/items/1a963f3dc87bc9bd9b79) ---- **💽Change log** - #2022/01/30 作成