Chrome 확장 프로그램에서 runtime.sendMessage 콜백이 제대로 작동하지 않는 이유

상황



Chrome 확장에서 content_scripts 측에서 BackgroundPage 측으로 sendMessage하면 콜백 인수가 undefined가됩니다.

증상




위 그림과 같이 backgroundPage에서 비동기 처리 (이번에는 chrome.tabs.getSelected)를 수행하고 그 결과를 콜백 함수의 인수에 넣고 싶습니다.

manifest.json
{
    "manifest_version": 2,
    "name": "sendMessage Test",
    "version": "0.1",
    "author": "Tachibana446",
    "description": "hoge",
    "browser_action": {
        "default_icon": {
            "38": "icon_38.png"
        },
        "default_title": "Do it!"
    },
    "permissions": ["tabs", "http://*/*", "https://*/*"],
    "background": {
        "scripts": ["background.js"],
        "presistent": false
    },
    "content_scripts": [{
        "matches": ["https://www.google.co.jp/*"],
        "js": ["content.js"]
    }]
}

content.js
chrome.runtime.sendMessage({
    message: "message"
}, function(response) {
    console.log(response);
    alert(response);
});


background.js
chrome.runtime.onMessage.addListener(
    function(request, sender, callback) {  // 1
        chrome.tabs.getSelected(function(tab) {  // 2
            callback(tab.title);
        });
        // 3
    }
);


(chrome.tabs.getSelected는 현재 열려있는 탭을 검색하는 함수입니다)
위의 샘플은 현재 탭의 제목을 경고 대화 상자에서 표시하는 프로그램이지만 실제로 실행하면 경고에는 undefined로 표시됩니다.

해설



우선, 대전제로서 javascript의 뭔가 인수에 function 건네주거나 하는 계의 것은 비동기군요.
그 근처를 이해하지 못했기 때문에 좀처럼 몰랐습니다.

htps : //로 ゔぇぺぺr. ch 로메. 코 m / 에 x 텐시 온 s / 룬 치메 # 에 ぇ
그리고 위의 이 페이지에 해설이 제대로 있어요.

Function to call (at most once) when you have a response. The argument should be any JSON-ifiable object. If you have more than one onMessage listener in the same document, then only one may send a response. 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).

The sendResponse parameter should be a function that looks like this:

function() {...};

unless you return true를 참조하십시오. 하지만 필자는 영어를 할 수 없기 때문에 엉망이지만,
"이 (sendMessage의 인수로 주어진 콜백) 함수는 값을 반환 할 때 무효화됩니다. 비동기 처리를 동기적으로 기다릴 때 true를 반환합니다."
라고 하는 것이 쓰여 있습니다.
즉, 비동기적인 처리를 하기 전에 함수의 마지막에 도달해 버려, 값(이 경우 명시적으로 return 되어 있지 않기 때문에 undef)가 돌려주어지기 때문에 거기서 처리가 무효가 되어 버렸던 것입니다.

구체적으로 설명합시다.
앞의 background.js를 참조하세요.// 1 그런데 리스너의 익명 함수가 시작됩니다.// 2 그런데, getSelected 함수 (의 인수에 주어진 익명 함수)가 비동기로 행해집니다.
그러나 비동기이므로, 그 처리는 일단 두고, 그대로 // 3 로 갑니다.// 3 이후, 리스너의 함수는 계속되지 않기 때문에, 여기서 리스너의 함수가 종료해, 디폴트치 (아마 undefined)가 돌려주어집니다.
값이 리턴되었기 때문에, 이 청취자 함수는 무효가 되어, 대기하고 있던 비동기의 getSelected 의 인수의 익명 함수도 처리되지 않습니다.
그대로 콜백이 호출되기 때문에 예상했던 탭의 제목을 얻을 수 없습니다.

해결책



공식 문서와 같이 명시적으로 true를 반환합시다.
방금 전의 // 3 곳에 return true 라고 쓸 뿐입니다.

background.js
chrome.runtime.onMessage.addListener(
    function(request, sender, callback) {  // 1
        chrome.tabs.getSelected(function(tab) {  // 2 
            callback(tab.title);
        });
        // 3
        return true;
    }
);

좋은 웹페이지 즐겨찾기