क्रोम एक्सटेंशन संदेश गुजर रहा है: प्रतिक्रिया नहीं भेजी गई


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 पैरामीटर का संदर्भ संग्रहीत करना महत्वपूर्ण है। इसके बिना, प्रतिक्रिया वस्तु के दायरे से बाहर हो जाती है और इसे कॉल नहीं किया जा सकता है। उस कोड के लिए धन्यवाद जिसने मुझे मेरी समस्या को ठीक करने की ओर संकेत किया!
ट्रिकीडिक

हो सकता है कि एक और उपाय यह है कि वादा के साथ एक async फ़ंक्शन के अंदर सब कुछ लपेटें और async विधियों की प्रतीक्षा करें?
एनरिक

जवाबों:


348

के लिए प्रलेखन सेchrome.runtime.onMessage.addListener :

ईवेंट श्रोता के वापस आने पर यह फ़ंक्शन अमान्य हो जाता है, जब तक कि आप ईवेंट श्रोता से सही रूप से वापस नहीं लौटते हैं, यह इंगित करने के लिए कि आप एसिंक्रोनस रूप से प्रतिक्रिया भेजना चाहते हैं (यह संदेश चैनल को दूसरे छोर पर तब तक खुला रखेगा जब तक कि sendResponse नहीं कहा जाता है)।

इसलिए आपको केवल यह इंगित return true;करने के getUrlsलिए कॉल के बाद जोड़ने की आवश्यकता है कि आप प्रतिक्रिया फ़ंक्शन को अतुल्यकालिक रूप से कॉल करेंगे।


यह सही है, मैंने अपने जवाब में इसे स्वचालित करने का एक तरीका जोड़ा
जिग मंडेल

62
इसके लिए +1। इस मुद्दे पर बहस करने की कोशिश में 2 दिन बर्बाद करने के बाद इसने मुझे बचाया है। मुझे विश्वास नहीं हो रहा है कि यह संदेश पासिंग गाइड में बिल्कुल भी उल्लेख नहीं किया गया है: developer.chrome.com/extensions/messaging
funforums

6
मैं स्पष्ट रूप से इस मुद्दे को पहले था; मुझे एहसास हुआ कि मैंने पहले ही इसे उखाड़ दिया था। यह पृष्ठ पर कहीं बड़े <blink>और <marquee>टैग में बोल्ड होना चाहिए ।
Qix - मोनासा

2
@funforums FYI करें, यह व्यवहार अब मैसेजिंग डॉक्यूमेंटेशन में प्रलेखित है (अंतर यहाँ है: codereview.chromium.org/1874133002/patch/80001/90002 )।
रोब डब्ल्यू

10
मैं शपथ लेता हूं कि यह मेरे द्वारा उपयोग किया गया सबसे अनचाहा एपीआई है।
माइकल्सवल्डन

8

स्वीकृत उत्तर सही है, मैं सिर्फ नमूना कोड जोड़ना चाहता था जो इसे सरल करता है। समस्या यह है कि एपीआई (मेरे विचार में) अच्छी तरह से डिज़ाइन नहीं किया गया है क्योंकि यह हमें डेवलपर्स को यह जानने के लिए मजबूर करता है कि क्या किसी विशेष संदेश को async संभाला जाएगा या नहीं। यदि आप कई अलग-अलग संदेशों को संभालते हैं तो यह एक असंभव कार्य हो जाता है क्योंकि आप कभी नहीं जानते हैं कि क्या कुछ फ़ंक्शन को डाउन-इन करने के बाद एक भेजा-भेजा सेंकंड को async कहा जाएगा या नहीं। इस पर विचार करो:

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

});

यह स्वचालित रूप से रिटर्न मान को संभालता है, भले ही आप संदेश को संभालने के लिए कैसे चुनें। ध्यान दें कि यह मानता है कि आप प्रतिक्रिया फ़ंक्शन को कॉल करना कभी नहीं भूलते हैं। यह भी ध्यान दें कि क्रोमियम हमारे लिए यह स्वचालित कर सकता है, मैं नहीं देखता कि वे क्यों नहीं थे।


एक मुद्दा यह है कि कभी-कभी आप प्रतिक्रिया फ़ंक्शन को कॉल नहीं करना चाहते हैं, और उन मामलों में आपको झूठ बोलना चाहिए । यदि आप नहीं करते हैं, तो आप Chrome को संदेश से जुड़े संसाधनों को मुक्त करने से रोक रहे हैं।
rsanchez

हाँ, यही कारण है कि मैंने कॉलबैक कॉल करना न भूलें। वह विशेष मामला जिसे आप मेंशन करते हैं, एक कन्वेंशन के द्वारा संभाला जा सकता है कि हैंडलर (हैंडलमैथोड 1 इत्यादि) "नो रिस्पॉन्स" केस को इंगित करने के लिए झूठे हैं (हालांकि ईद सिर्फ हमेशा एक प्रतिक्रिया, यहां तक ​​कि एक खाली भी है)। इस तरह से रखरखाव की समस्या केवल उन विशेष "नो रिटर्न" मामलों के लिए स्थानीयकृत है।
जिग मंडेल

8
पहिया का फिर से आविष्कार न करें। पदावनत chrome.extension.onRequest/ chrome.exension.sendRequestविधियाँ वैसा ही व्यवहार करती हैं जैसा आप वर्णन करते हैं। इन विधियों को पदावनत किया जाता है क्योंकि यह पता चलता है कि कई विस्तार डेवलपर्स ने संदेश पोर्ट को बंद नहीं किया था। वर्तमान एपीआई (आवश्यक return true) एक बेहतर डिजाइन है, क्योंकि असफल रहना चुपचाप लीक करने से बेहतर है।
रॉब डब्ल्यू

@RobW लेकिन फिर मुद्दा क्या है? मेरा जवाब देव को सच को भूलने से रोकता है।
जिग मैंडेल

@ZigMandel यदि आप प्रतिक्रिया भेजना चाहते हैं, तो उपयोग करें return true;। यदि कॉल सिंक है, तो यह पोर्ट को क्लीन-अप होने से नहीं रोकता है, जबकि async कॉल अभी भी सही तरीके से संसाधित हो रहे हैं। इस उत्तर में कोड बिना किसी स्पष्ट लाभ के अनावश्यक जटिलता का परिचय देता है।
रोब डब्ल्यू

2

आप बिना कॉलबैक के फ़ायरफ़ॉक्स तरीके से क्रोम और एफएफ दोनों में यह काम करने के लिए मेरी लाइब्रेरी https://github.com/lawlietmester/webextension का उपयोग कर सकते हैं ।

आपका कोड ऐसा दिखेगा:

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 }); });
}) );
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.