ข้อความส่วนขยายของ 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})
    }
  });

}

ตอนนี้ถ้าฉันส่งการตอบกลับก่อนการโทร ajax ในgetUrlsฟังก์ชั่นการตอบสนองจะถูกส่งเรียบร้อยแล้ว แต่ในวิธีการที่ประสบความสำเร็จของการโทร ajax เมื่อฉันส่งการตอบกลับมันไม่ได้ส่งเมื่อฉันเข้าสู่การดีบักฉันจะเห็นว่า พอร์ตนั้นเป็นโมฆะภายในโค้ดของsendResponseฟังก์ชัน


การจัดเก็บการอ้างอิงไปยังพารามิเตอร์ sendResponse เป็นสิ่งสำคัญ ก็ไม่มีวัตถุการตอบสนองออกไปจากขอบเขตและไม่สามารถเรียก ขอบคุณสำหรับรหัสที่บอกเป็นนัยถึงการแก้ไขปัญหาของฉัน!
TrickiDicki

อาจเป็นวิธีการแก้ปัญหาอื่นคือการห่อทุกอย่างภายในฟังก์ชั่น async ด้วย Promise และการโทรรอวิธี async หรือไม่
Enrique

คำตอบ:


348

จากเอกสารสำหรับchrome.runtime.onMessage.addListener :

ฟังก์ชั่นนี้จะใช้งานไม่ได้เมื่อผู้ฟังเหตุการณ์ส่งคืนเว้นแต่คุณส่งคืนจริงจากผู้ฟังเหตุการณ์เพื่อระบุว่าคุณต้องการส่งการตอบกลับแบบอะซิงโครนัส (สิ่งนี้จะทำให้ช่องข้อความเปิดอยู่

ดังนั้นคุณต้องเพิ่มreturn true;หลังจากการเรียกgetUrlsเพื่อระบุว่าคุณจะเรียกใช้ฟังก์ชันการตอบสนองแบบอะซิงโครนัส


นี่ถูกต้องแล้วฉันได้เพิ่มวิธีการทำสิ่งนี้โดยอัตโนมัติในคำตอบของฉัน
Zig Mandel

62
+1 สำหรับสิ่งนี้ มันได้ช่วยฉันหลังจากใช้เวลา 2 วันเพื่อพยายามแก้ไขปัญหานี้ ฉันไม่อยากจะเชื่อเลยว่าสิ่งนี้ไม่ได้กล่าวถึงในคู่มือการส่งข้อความที่: developer.chrome.com/extensions/messaging
funforums

6
เห็นได้ชัดว่าฉันมีปัญหานี้มาก่อน กลับมาอีกครั้งเพื่อทราบว่าข้าได้ลงมือทำสิ่งนี้แล้ว สิ่งนี้จะต้องเป็นตัวหนาในขนาดใหญ่<blink>และ<marquee>แท็กบางแห่งในหน้า
Qix - MONICA ถูกยกเลิกเมื่อ

2
@funforums FYI พฤติกรรมนี้ได้รับการบันทึกไว้ในเอกสารการส่งข้อความแล้ว (ความแตกต่างอยู่ที่นี่: codereview.chromium.org/1874133002/patch/80001/90002 )
Rob W

10
ฉันสาบานว่านี่เป็น API ที่ไม่ได้ใช้งานง่ายที่สุดที่ฉันเคยใช้
michaelsnowden

8

คำตอบที่ยอมรับนั้นถูกต้องฉันแค่ต้องการเพิ่มโค้ดตัวอย่างที่ทำให้สิ่งนี้ง่ายขึ้น ปัญหาคือว่า API (ในมุมมองของฉัน) ไม่ได้รับการออกแบบมาอย่างดีเพราะมันบังคับให้เรานักพัฒนาทราบว่าข้อความเฉพาะจะได้รับการจัดการ async หรือไม่ หากคุณจัดการกับข้อความที่แตกต่างกันสิ่งนี้จะกลายเป็นงานที่เป็นไปไม่ได้เพราะคุณไม่เคยรู้เลยว่าฟังก์ชั่นบางอย่างที่ลึกลงไป sendResponse ที่ส่งผ่านจะถูกเรียกว่า async หรือไม่ พิจารณาสิ่งนี้:

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

ฉันจะรู้ได้อย่างไรว่าhandleMethod1การโทรลึกจะเป็นแบบซิงค์หรือไม่? คนที่แก้ไขจะhandleMethod1รู้ได้อย่างไรว่ามันจะทำให้ผู้โทรเสียโดยการแนะนำบางอย่างให้เป็น async

ทางออกของฉันคือ:

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

ใช่นั่นเป็นเหตุผลว่าทำไมฉันถึงบอกว่าอย่าลืมโทรกลับ กรณีพิเศษที่คุณสามารถจัดการได้โดยมีการจัดการที่ตัวจัดการ (handleMethod1 ฯลฯ ) คืนค่าเท็จเพื่อระบุกรณี "no response" (แม้ว่า Id จะทำการตอบกลับเสมอแม้จะว่างเปล่าก็ตาม) วิธีนี้ปัญหาการบำรุงรักษาจะถูกแปลเป็นภาษาท้องถิ่นสำหรับกรณีพิเศษ "no return"
Zig Mandel

8
อย่าประดิษฐ์วงล้อใหม่ วิธีการที่เลิกใช้chrome.extension.onRequest/ chrome.exension.sendRequestวิธีการทำงานตรงตามที่คุณอธิบาย วิธีการเหล่านี้ถูกคัดค้านเนื่องจากปรากฏว่าผู้พัฒนาส่วนขยายจำนวนมากไม่ได้ปิดพอร์ตข้อความ API ปัจจุบัน (ต้องมีreturn true) เป็นการออกแบบที่ดีกว่าเนื่องจากความล้มเหลวอย่างหนักนั้นดีกว่าการรั่วอย่างเงียบ ๆ
Rob W

@ RobW แต่ปัญหาคืออะไรแล้ว? คำตอบของฉันทำให้ผู้พัฒนาไม่สามารถลืมความจริงคืนได้
Zig Mandel

@ZigMandel return true;หากคุณต้องการที่จะส่งการตอบสนองการใช้งานเพียงแค่ ไม่ป้องกันพอร์ตจากการล้างข้อมูลหากการโทรนั้นถูกซิงค์ขณะที่การโทรแบบ async ยังคงถูกประมวลผลอย่างถูกต้อง รหัสในคำตอบนี้นำเสนอความซับซ้อนที่ไม่จำเป็นเพื่อประโยชน์ที่ไม่ชัดเจน
Rob W

2

คุณสามารถใช้ห้องสมุดของฉันhttps://github.com/lawlietmester/webextensionเพื่อให้การทำงานนี้ทั้งใน Chrome และ FF ด้วยวิธี Firefox โดยไม่ต้องโทรกลับ

รหัสของคุณจะมีลักษณะดังนี้:

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.