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.