admin管理员组

文章数量:1416651

For a while I've been using chrome.runtime.sendMessage(message, callback) for messages from my content script that need to run a callback function after receiving a response from the background.

I've also got a long-lived connection that is used for sending messages from the background to the content script (without it being a response to a message initiated by the content script):

backgroundPort = chrome.runtime.connect({ name: "contentScript" });
backgroundPort.onMessage.addListener(function(message){
  if (message["action"] == "something"){
    // Do stuff
  }
});

As far as I can tell, backgroundPort.postMessage() does not support callback messages which means messages that have a callback need to use chrome.runtime.sendMessage().

The problem with this approach is that there is a lot of overhead in setting up a new connection between content script/background for each message, so I'm trying to get callback functionality into the existing long-lived connection through backgroundPort, but it gets kind of messy.

Anyone who's been able to e up with an elegant solution?

For a while I've been using chrome.runtime.sendMessage(message, callback) for messages from my content script that need to run a callback function after receiving a response from the background.

I've also got a long-lived connection that is used for sending messages from the background to the content script (without it being a response to a message initiated by the content script):

backgroundPort = chrome.runtime.connect({ name: "contentScript" });
backgroundPort.onMessage.addListener(function(message){
  if (message["action"] == "something"){
    // Do stuff
  }
});

As far as I can tell, backgroundPort.postMessage() does not support callback messages which means messages that have a callback need to use chrome.runtime.sendMessage().

The problem with this approach is that there is a lot of overhead in setting up a new connection between content script/background for each message, so I'm trying to get callback functionality into the existing long-lived connection through backgroundPort, but it gets kind of messy.

Anyone who's been able to e up with an elegant solution?

Share Improve this question edited Dec 10, 2014 at 6:52 Woodgnome asked Dec 9, 2014 at 15:54 WoodgnomeWoodgnome 2,4015 gold badges29 silver badges54 bronze badges 2
  • So your problem is to have a postMessage(message, callback) function? This can be arranged, in principle. – Xan Commented Dec 9, 2014 at 16:19
  • For a long-lived connection, yes. I could send messages back from the background with message["action"] = "doStuffCallback", but I don't think it's a good solution. – Woodgnome Commented Dec 9, 2014 at 16:30
Add a ment  | 

2 Answers 2

Reset to default 5

If you look at your background script as socket server (Port object is basically simulating sockets), you could use something called "acknowledgments" that is used in Nodejs, socket.io library.

Basically you store your callbacks functions inside object by giving them unique identifier that you pass to the other side.

// on the sending side

// this variable will store callbacks
var acknowledgments = {};

var port = chrome.runtime.connect();

// this variable will be unique callback idetifier
var address = Math.random().toString(36);

// You create acknowledgment by identifying callback
acknowledgments[address] = function (data) {
    // callback function
    // do what you like with result data
};

port.postMessage({
    acknowledgment: address,
    data: messageData
})

port.onMessage.addListener(function (message) {
    var callback = acknowledgments[message.acknowledgment];
    if (callback) {
        callback(message.data);
        // don't forget to unset acknowledgment, because it's 
        // supposed to exists only until it's been called.
        delete acknowledgments[message.acknowledgment];
        return;
    }

    // other message types handling
});

Other side passes result with that unique identifier returned, by which you an identify what to do.

// on the listening side
chrome.runtime.onConnect.addListener(function (port) {
    port.onMessage.addListener(function (message) {
        if (message.acknowledgment) {
            // do stuff you need to do
            // then return result
            port.postMessage({
                acknowledgment: message.acknowledgment
                data: stuffYouHaveDoneResult
            });
        }
    });
});

chrome.runtime.sendMessage works using "Port" object at lower level so the actual implementation of sendMessage callback should be similar to this in principle.

To put the callback part in a function and return a response value, we can use Promise(code adapted from @antanas_sepikas'):

function myPostMessage(message){
    var address = Math.random().toString(36);
    message.acknowledgment =address;
    acknowledgments[address] = function (data) {
      // callback function
      // do what you like with result data
      return some_data;
    };
    port.postMessage(message)
    let initPromise = new Promise((resolve, reject) => {
      port.onMessage.addListener(function (response) {
          var callback = acknowledgments[response.acknowledgment];
          if (callback) {
              delete acknowledgments[response.acknowledgment];
              resolve(callback(response.data));
              // don't forget to unset acknowledgment, because it's
              // supposed to exists only until it's been called.
          }
          // other message types handling                                                                                                                          });
    })
    return initPromise;
  }

var res = await myPostMessage(msg);

And you can also add a timeout if needed.

本文标签: javascriptChrome extension longlived message connectionhow to use callback functionsStack Overflow