當年(其實也就是幾個月以前)在製作 extension 某個功能的時候需要呼叫 chrome.tabs
API 來拿一些資料再送回去給 content
,於是就寫出了像下面這樣的 code:
/* file: content_script.js */
chrome.runtime.postMessage({action: 'tabs information'}, function(res) {
/**
* Do something with tabs information
*/
});
/* file: background.js */
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action === 'tabs information') {
// Query all tabs for information
chrome.tabs.query({}, function (tabs) {
var info = [];
tabs.forEach(function (tab) {
info.push({
id: tab.id,
title: tab.title,
url: tab.url
});
});
// Send it back with `sendResponse` function
sendResponse(info);
});
}
});
看起來沒什麼大問題,但很遺憾的,這樣並不 work 。
我當時百思不得其解,用 Chrome DevTools 不停的 debug,花了一個下午的時間直到我在 chrome.runtime.onMessage
的文件裡面看到…
This function becomes invalid when the event listener returns, unless you return true from the event listener to indicate you wish to send a response asynchronously (this will keep the message channel open to the other end until
sendResponse
is called).
看來是預設的情況之下, event listener 跑完就會把 sendResponse
用的 message channel 關起來,因此如果你需要 sendResponse
是在 asynchronous 的情況下被呼叫的話(基本上大部分的 chrome.*
API 都是 asynchronous callback 的形式),在 event listener 的最後需要 return true
。
所以只要稍微修改一下 background.js
就行了!
/* file: background.js */
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action === 'tabs information') {
// Query all tabs for information
chrome.tabs.query({}, function (tabs) {
var info = [];
tabs.forEach(function (tab) {
info.push({
id: tab.id,
title: tab.title,
url: tab.url
});
});
// Send it back with `sendResponse` function
sendResponse(info);
});
// Keep the message channel open until the `sendResponse` is called
return true;
}
});