Chrome Uzantı İletisi iletiliyor: yanıt gönderilmedi


151

İçerik komut dosyası ve uzantı arasında ileti iletmeye çalışıyorum

İşte içerik betiğinde ne var

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

Ve arka plan komut dosyasında

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

}

Şimdi, getUrlsfonksiyondaki ajax çağrısından önce yanıtı gönderirsem, yanıt başarıyla gönderilir, ancak cevabı gönderdiğimde ajax çağrısının başarı yönteminde, onu göndermez, hata ayıklamaya gittiğimde bunu görebiliyorum bağlantı noktası sendResponseişlev kodunun içinde boş .


Bir gönderme sendResponse parametresine depolamak çok önemlidir. Bu olmadan, yanıt nesnesi kapsam dışına çıkar ve çağrılamaz. Sorunumu düzeltmem için beni ima eden kod için teşekkürler!
TrickiDicki

belki başka bir çözüm, zaman uyumsuz bir işlev içindeki her şeyi Promise ile sarmak ve zaman uyumsuz yöntemler için beklemek?
Enrique

Yanıtlar:


348

İçin belgelerdenchrome.runtime.onMessage.addListener :

Bu işlevi olay dinleyicisi geri döndüğünde, olay dinleyicisinden eşzamansız olarak bir yanıt göndermek istediğinizi belirtmek için geri dönmezseniz geçersiz olur (sendResponse çağrılıncaya kadar ileti kanalını diğer uca açık tutar).

Bu nedenle , yanıt işlevini eşzamansız olarak çağıracağınızı belirtmek return true;için aramadan sonra eklemeniz yeterlidir getUrls.


bu doğru,
Zig Mandel

62
Bunun için +1. Bu sorunu ayıklamaya çalışırken 2 gün israf ettikten sonra beni kurtardı. Bunun şu mesaj
iletme

6
Görünüşe göre bu sorunu daha önce yaşadım; bunu zaten iptal ettiğimin farkına vardım. Bu ihtiyaçlar, büyük kalın olması <blink>ve <marquee>sayfadaki etiketleri yerde.
Qix - MONICA

2
@funforums FYI, bu davranış artık ileti belgelerinde belgelenmiştir (fark burada: codereview.chromium.org/1874133002/patch/80001/90002 ).
Rob W

10
Yemin ederim ki şimdiye kadar kullandığım en sezgisel olmayan API.
michaelsnowden

8

Kabul edilen cevap doğrudur, sadece bunu basitleştiren örnek kod eklemek istedim. Sorun, API (benim görüşüme göre) iyi tasarlanmamış olmasıdır, çünkü bizi geliştiricileri belirli bir mesajın zaman uyumsuz işlenip işlenmeyeceğini bilmeye zorlar. Birçok farklı iletiyi işlerseniz, bu imkansız bir görev haline gelir, çünkü bazı işlevlerin derinliklerine inip geçmediğini asla bilemezsiniz. Bunu düşün:

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

handleMethod1Çağrının derinliğinin zaman uyumsuz olup olmadığını nasıl anlayabilirim ? Değiştiren bir kişi handleMethod1, zaman uyumsuz bir şey getirerek arayanı kıracağını nasıl bilebilir?

Benim çözümüm şudur:

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

});

Bu, iletiyi nasıl işlemeyi seçtiğinize bakılmaksızın, dönüş değerini otomatik olarak işler. Bunun, yanıt işlevini çağırmayı asla unutmayacağınızı varsaydığını unutmayın. Ayrıca kromun bunu bizim için otomatikleştirebileceğini de unutmayın, neden yapmadıklarını anlamıyorum.


Bir sorun, bazen yanıt işlevini çağırmak istemeyeceğinizdir ve bu durumlarda false döndürmeniz gerekir . Bunu yapmazsanız, Chrome'un mesajla ilişkili kaynakları boşaltmasını engelliyorsunuz demektir.
rchezchez

Evet, bu yüzden geri aramayı unutmamamı söyledim. Bu özel durum, işleyicinin (handleMethod1 vb.) "Yanıt yok" durumunu belirtmek için yanlış döndürdüğü (Id yerine her zaman boş bir yanıt bile ver) kuralına göre ele alınabilir. Bu şekilde, sürdürülebilirlik sorunu yalnızca bu özel "geri dönüşsüz" vakalarda yerelleştirilir.
Zig Mandel

8
Tekerleği yeniden icat etme. Kullanımdan kaldırılmış chrome.extension.onRequest/ chrome.exension.sendRequestyöntemler tam olarak sizin tanımladığınız şekilde davranır. Birçok uzantı geliştiricisinin ileti bağlantı noktasını KAPATMADIĞI nedeniyle bu yöntemler kullanımdan kaldırılmıştır. Mevcut API (gerektiren return true) daha iyi bir tasarımdır, çünkü zor başarısız olmak sessizce sızmaktan daha iyidir.
Rob W

@RobW ama sorun ne o zaman? cevabım geliştiricinin gerçeğe dönmeyi unutmasını engelliyor.
Zig Mandel

@ZigMandel Yanıt göndermek istiyorsanız, kullanın return true;. Arama senkronize edilirken bağlantı noktasının temizlenmesini engellemezken, zaman uyumsuz çağrılar hala doğru şekilde işlenir. Bu yanıttaki kod, görünür bir fayda sağlamak için gereksiz karmaşıklık getirir.
Rob W

2

Https://github.com/lawlietmester/webextension kitaplığımı kullanarak bu çalışmayı Chrome ve FF'de Firefox ile geri arama yapmadan kullanabilirsiniz.

Kodunuz şöyle görünecektir:

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 }); });
}) );
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.