Thông báo tiện ích mở rộng của Chrome: phản hồi không được gửi


151

Tôi đang cố gắng chuyển tin nhắn giữa tập lệnh nội dung và phần mở rộng

Đây là những gì tôi có trong kịch bản nội dung

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

Và trong kịch bản nền tôi có

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})
    }
  });

}

Bây giờ nếu tôi gửi phản hồi trước lệnh gọi ajax trong getUrlshàm, phản hồi sẽ được gửi thành công, nhưng trong phương thức thành công của cuộc gọi ajax khi tôi gửi phản hồi thì nó không gửi, khi tôi gỡ lỗi, tôi có thể thấy rằng cổng là null bên trong mã cho sendResponsechức năng.


Lưu trữ một tham chiếu đến tham số sendResponse là rất quan trọng. Không có nó, đối tượng phản hồi đi ra khỏi phạm vi và không thể được gọi. Cảm ơn mã đã gợi ý cho tôi để khắc phục vấn đề của tôi!
TrickiDicki

có lẽ một giải pháp khác là bọc mọi thứ bên trong một hàm async bằng Promise và gọi chờ các phương thức async?
Enrique

Câu trả lời:


348

Từ tài liệu chochrome.runtime.onMessage.addListener :

Hàm này trở nên không hợp lệ khi trình lắng nghe sự kiện trả về, trừ khi bạn trả về true từ trình nghe sự kiện để cho biết bạn muốn gửi phản hồi không đồng bộ (điều này sẽ giữ cho kênh thông báo mở sang đầu kia cho đến khi sendResponse được gọi).

Vì vậy, bạn chỉ cần thêm return true;sau cuộc gọi để getUrlscho biết rằng bạn sẽ gọi chức năng trả lời không đồng bộ.


Điều này là chính xác, tôi đã thêm một cách để tự động hóa điều này trong câu trả lời của mình
Zig Mandel

62
+1 cho điều này. Nó đã cứu tôi sau khi lãng phí 2 ngày cố gắng gỡ lỗi vấn đề này. Tôi không thể tin rằng điều này hoàn toàn không được đề cập trong hướng dẫn chuyển tin nhắn tại: developer.chrome.com/extensions/messaging
funforums

6
Tôi rõ ràng đã có vấn đề này trước đây; trở lại để nhận ra tôi đã nâng cao điều này. Điều này cần phải được in đậm <blink><marquee>thẻ ở đâu đó trên trang.
Qix - MONICA ĐƯỢC PHÂN PHỐI

2
@funforums FYI, hành vi này hiện được ghi lại trong tài liệu nhắn tin (sự khác biệt là ở đây: codereview.chromium.org/1874133002/patch/80001/90002 ).
Rob W

10
Tôi thề đây là API không trực quan nhất tôi từng sử dụng.
michaelsnowden

8

Câu trả lời được chấp nhận là chính xác, tôi chỉ muốn thêm mã mẫu đơn giản hóa việc này. Vấn đề là API (theo quan điểm của tôi) không được thiết kế tốt vì nó buộc các nhà phát triển phải biết liệu một thông điệp cụ thể có được xử lý không đồng bộ hay không. Nếu bạn xử lý nhiều thư khác nhau, điều này sẽ trở thành một nhiệm vụ bất khả thi vì bạn không bao giờ biết nếu sâu bên dưới một số chức năng, sendResponse được truyền vào sẽ được gọi là async hay không. Xem xét điều này:

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

Làm thế nào tôi có thể biết nếu sâu bên dưới handleMethod1cuộc gọi sẽ không đồng bộ hay không? Làm thế nào một người có thể sửa đổi handleMethod1biết rằng nó sẽ phá vỡ một người gọi bằng cách giới thiệu một cái gì đó không đồng bộ?

Giải pháp của tôi là thế này:

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;
    }

});

Điều này tự động xử lý giá trị trả về, bất kể bạn chọn cách xử lý tin nhắn như thế nào. Lưu ý rằng điều này giả định rằng bạn không bao giờ quên gọi hàm phản hồi. Cũng lưu ý rằng crom có ​​thể tự động hóa điều này cho chúng tôi, tôi không hiểu tại sao họ không làm vậy.


Một vấn đề là đôi khi bạn sẽ không muốn gọi hàm phản hồi và trong những trường hợp đó bạn sẽ trả về false . Nếu bạn không, bạn đang ngăn Chrome giải phóng các tài nguyên liên quan đến tin nhắn.
rsanchez

vâng, đó là lý do tại sao tôi nói không quên gọi lại. Trường hợp đặc biệt mà bạn có thể được xử lý bằng cách có một quy ước rằng trình xử lý (handleMethod1, v.v.) trả về sai để chỉ trường hợp "không phản hồi" (mặc dù Id chỉ luôn luôn trả lời, thậm chí là trống). Bằng cách này, vấn đề bảo trì chỉ được tập trung vào những trường hợp "không trả lại" đặc biệt đó.
Zig Mandel

8
Đừng phát minh lại bánh xe. Các phương thức chrome.extension.onRequest/ deprecated chrome.exension.sendRequesthành xử chính xác như bạn mô tả. Các phương thức này không được chấp nhận vì hóa ra nhiều nhà phát triển tiện ích mở rộng KHÔNG đóng cổng thông báo. API hiện tại (yêu cầu return true) là một thiết kế tốt hơn, bởi vì thất bại khó hơn là rò rỉ âm thầm.
Rob W

@RobW nhưng vấn đề là gì? câu trả lời của tôi ngăn các dev quên trở về đúng.
Zig Mandel

@ZigMandel Nếu bạn muốn gửi phản hồi, chỉ cần sử dụng return true;. Nó không ngăn chặn cổng được dọn sạch nếu cuộc gọi được đồng bộ hóa, trong khi các cuộc gọi không đồng bộ vẫn đang được xử lý chính xác. Mã trong câu trả lời này giới thiệu sự phức tạp không cần thiết cho không có lợi ích rõ ràng.
Rob W

2

Bạn có thể sử dụng thư viện của tôi https://github.com/lawlietmester/webextension để làm cho điều này hoạt động trong cả Chrome và FF với Firefox mà không cần gọi lại.

Mã của bạn sẽ trông như sau:

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 }); });
}) );
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.