Chrome 확장 메시지 전달 : 응답이 전송되지 않음


151

콘텐츠 스크립트와 확장 프로그램 사이에 메시지를 전달하려고합니다.

내용 스크립트에서 내가 가진 것입니다

chrome.runtime.sendMessage({type: "getUrls"}, function(response) {
  console.log(response)
});

그리고 백그라운드 스크립트에서

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    if (request.type == "getUrls"){
      getUrls(request, sender, sendResponse)
    }
});

function getUrls(request, sender, sendResponse){
  var resp = sendResponse;
  $.ajax({
    url: "http://localhost:3000/urls",
    method: 'GET',
    success: function(d){
      resp({urls: d})
    }
  });

}

이제 getUrls함수 에서 ajax 호출 전에 응답을 보내면 응답이 성공적으로 전송되지만 응답을 보낼 때 ajax 호출의 성공 방법에서는 보내지 않습니다. 디버깅을 할 때 볼 수 있습니다. sendResponse함수 코드 내에서 포트가 널 입니다.


sendResponse 매개 변수에 대한 참조를 저장하는 것이 중요합니다. 그렇지 않으면 응답 오브젝트가 범위를 벗어나 호출 할 수 없습니다. 내 문제를 해결하는 데 도움이되는 코드에 감사드립니다!
TrickiDicki

어쩌면 또 다른 해결책은 Promise를 사용하여 비동기 함수 내부의 모든 것을 감싸고 비동기 메소드를 기다리는 것입니다.
Enrique

답변:


348

에서 의 문서chrome.runtime.onMessage.addListener :

이 함수는 이벤트 리스너에서 true를 반환하여 응답을 비동기식으로 보내려는 것을 나타내지 않는 한 이벤트 리스너가 반환 될 때 유효하지 않습니다 (이는 sendResponse가 호출 될 때까지 메시지 채널을 다른 쪽 끝으로 열어 둡니다).

따라서 return true;호출 후에 getUrls응답 함수를 비동기 적으로 호출 할 것임을 나타 내기 위해 추가 하면됩니다.


이것이 맞습니다. 제 답변에 이것을 자동화하는 방법을 추가했습니다
Zig Mandel

62
이것을 위해 +1. 이 문제를 디버깅하는 데 2 ​​일을 낭비한 후 저를 구했습니다. :이은에 메시지 통과 가이드에서 전혀 언급되지 않는다는 것을 믿을 수 없다 developer.chrome.com/extensions/messaging
funforums

6
나는 분명히이 문제를 전에 보았다; 내가 이미 이것을지지했다는 것을 깨닫기 위해 돌아왔다. 이 요구는 큰 굵게 할 수 <blink><marquee>페이지에 태그 곳.
Qix-MONICA가 MISTREATED

2
@funforums 참고로,이 동작은 이제 메시징 문서에 설명되어 있습니다 (차이는 codereview.chromium.org/1874133002/patch/80001/90002 ).
Rob W

10
나는 이것이 내가 사용했던 가장 직관적이지 않은 API라고 맹세한다.
michaelsnowden

8

허용 된 답변은 정확합니다. 단순히 샘플 코드를 추가하고 싶었습니다. 문제는 API (내 관점에서)가 특정 메시지가 비동기로 처리되는지 여부를 개발자가 알도록 강제하기 때문에 잘 설계되지 않았다는 것입니다. 여러 가지 다른 메시지를 처리하는 경우 전달 된 sendResponse를 일부 함수의 깊이에 대해 알지 못하기 때문에 불가능한 작업이됩니다. 이걸 고려하세요:

chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {
if (request.method == "method1") {
    handleMethod1(sendResponse);
}

깊은 handleMethod1통화가 비동기 인지 여부를 어떻게 알 수 있습니까? 수정 한 사람이 handleMethod1비동기식으로 무언가를 도입하여 발신자를 중단 시킬 것임을 어떻게 알 수 있습니까?

내 해결책은 이것입니다 :

chrome.extension.onMessage.addListener(function (request, sender, sendResponseParam) {

    var responseStatus = { bCalled: false };

    function sendResponse(obj) {  //dummy wrapper to deal with exceptions and detect async
        try {
            sendResponseParam(obj);
        } catch (e) {
            //error handling
        }
        responseStatus.bCalled= true;
    }

    if (request.method == "method1") {
        handleMethod1(sendResponse);
    }
    else if (request.method == "method2") {
        handleMethod2(sendResponse);
    }
    ...

    if (!responseStatus.bCalled) { //if its set, the call wasn't async, else it is.
        return true;
    }

});

메시지 처리 방법에 관계없이 반환 값을 자동으로 처리합니다. 이것은 응답 함수를 호출하는 것을 잊지 않는 것으로 가정합니다. 또한 크롬이 우리를 위해 이것을 자동화했을 수 있다는 점에 유의하십시오.


한 가지 문제는 때로는 응답 함수를 호출하지 않으려는 경우 false 를 반환해야한다는 것 입니다. 그렇지 않으면 Chrome이 메시지와 관련된 리소스를 확보하지 못하게됩니다.
rsanchez

그렇기 때문에 콜백을 호출하는 것을 잊지 마십시오. 처리기 (handleMethod1 등)가 "응답 없음"사례를 나타 내기 위해 false를 반환하는 규칙을 사용하여 이러한 특수한 경우를 처리 할 수 ​​있습니다 (ID는 항상 빈 상태 인 경우에도 응답을합니다). 이러한 방식으로 유지 보수성 문제는 특별한 "반품 불가"사례에만 국한됩니다.
Zig Mandel

8
바퀴를 다시 발명하지 마십시오. 더 이상 사용되지 않는 chrome.extension.onRequest/ chrome.exension.sendRequest메소드는 설명대로 작동합니다. 많은 확장 개발자가 메시지 포트를 닫지 않았기 때문에 이러한 방법은 더 이상 사용되지 않습니다. 현재 API (필수 return true)는 더 나은 디자인입니다. 실패하는 것이 조용히 누출되는 것보다 낫기 때문입니다.
Rob W

@RobW 그러나 문제는 무엇입니까? 내 대답은 개발자가 true를 반환하는 것을 잊어 버리지 못하게합니다.
Zig Mandel

@ZigMandel 응답을 보내려면을 사용하십시오 return true;. 비동기 호출이 여전히 올바르게 처리되는 동안 호출이 동기화 된 경우 포트가 정리되지 않습니다. 이 답변의 코드는 명백한 이점이없는 불필요한 복잡성을 소개합니다.
Rob W

2

내 라이브러리 https://github.com/lawlietmester/webextension 을 사용하여 콜백없이 Firefox 방식으로 Chrome 및 FF 에서이 작업을 수행 할 수 있습니다 .

코드는 다음과 같습니다.

Browser.runtime.onMessage.addListener( request => new Promise( resolve => {
    if( !request || typeof request !== 'object' || request.type !== "getUrls" ) return;

    $.ajax({
        'url': "http://localhost:3000/urls",
        'method': 'GET'
    }).then( urls => { resolve({ urls }); });
}) );
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.