
chrome.runtime.onMessage.addListenerからのresponseがundefined
2022/03/27
chrome extensionの開発で、background.jsからのchrome.runtimeによるresponseがundefinedになる問題に少しつまづいたのでメモとして残しておきます。
undefinedが返ってくるcode
実際にundefinedが返ってくるbackground.jscodeのchrome.runtime部分は以下のとおりです。
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
const res = await somePromiseFunction();
sendResponse(res);
});chrome.runtime.sendMessage('test', (res) => {
console.log(res) // undefined
});return trueを追記してみる
MDNドキュメントによると...
either keep a reference to the sendResponse() argument and return true from the listener function. You will then be able to call sendResponse() after the listener function has returned.
listener function内でreturn trueをしないと、非同期的な処理では先にportが閉じてしまう(sendResponseが使えなくなる)ようです。
ということでbackground.jsにreturn trueを追記してみます。
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
const res = await somePromiseFunction();
sendResponse(res);
return true; // <- ここに追記
});このtrueは非同期でメッセージ送るからsendResponseが呼ばれるまではport空けといてほしいことをchrome runtimeに伝えるためのtrueです。
これでundefined問題は解決...
と言いたいところですが、これだけだとまだsendResponseは機能しません。
async, awaitをやめる
勘が良い人は気づいているかもしれませんが、listener functionをasyncにしておくと、addListenerにはtrueがreturnされません。
つまり、async関数は実行時にtrueではなくPromise<boolean>をすぐに返すので、結局portが閉じ、sendResponseが使えなくなるというわけです。
したがって、最終的に正しいcodeは以下の通りになります。
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
somePromiseFunction().then((res) => {
sendResponse(res);
});
return true;
});somePromiseFunctionの処理中にtrueを返せてるので、後からでも問題なくsendResponseを使用できます。
おわり
browserによってはPromiseをreturnするだけでよさそう(chromeは現時点ではまだ対応してない)
