thumbnail

chrome.runtime.onMessage.addListenerからのresponseがundefined

2022/03/27

chrome extensionの開発で、background.jsからのchrome.runtimeによるresponseがundefinedになる問題に少しつまづいたのでメモとして残しておきます。

undefinedが返ってくるcode

実際にundefinedが返ってくるbackground.jscodeのchrome.runtime部分は以下のとおりです。

background.js
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
  const res = await somePromiseFunction();
  sendResponse(res);
});
front.js
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.jsreturn trueを追記してみます。

background.js
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は以下の通りになります。

background.js
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  somePromiseFunction().then((res) => {
    sendResponse(res);
  });
  return true;
});

somePromiseFunctionの処理中にtrueを返せてるので、後からでも問題なくsendResponseを使用できます。

おわり

browserによってはPromiseをreturnするだけでよさそう(chromeは現時点ではまだ対応してない)

author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

Profile

author picture

Mitsuru Takahashi

京都市内にてフリーランスエンジニアとして活動しています。

detail

© 2022 mitsuru takahashi