0

In my eventPage.js(background, persistent=false):

chrome.runtime.onMessage.addListener(async function(request, sender, sendResponse) {

    await new Promise((resolve, reject) => { 
        chrome.downloads.search({id: files[i]}, function (item) { 
            sendfiles.push(item[0].id);
            resolve();
        });
    });

    console.log(sendfiles); // contains (item[0].id), means it did wait.

    sendResponse("hello"); // Should send hello back

    return true;
});

And in my popup.js:

chrome.runtime.sendMessage("",(response) => {
    alert(response); // alerts undefinded instead of hello
});

My error: Unchecked runtime.lastError: The message port closed before a response was received.

As written in the comments of the code, It should respond to the request from the popup.js with "hello", but because it waited in the Promise, I get the error and the response becomes undefinded before I can sendResponse my "hello" (I'm guessing this is what happening)..

wOxxOm
  • 65,848
  • 11
  • 132
  • 136
Flafy
  • 176
  • 1
  • 3
  • 15
  • 1
    We cannot flag this question as a duplicate since the following one has no accepted answer, but it looks close to your problem? [chrome.runtime.onMessage response with async await](https://stackoverflow.com/a/46628145/1913729) – blex Feb 22 '20 at 15:38

1 Answers1

2

This function becomes invalid when the event listener returns, unless you return true from the event listener to indicate you wish to send a response asynchronously (this will keep the message channel open to the other end until sendResponse is called).

From here

But using async-await, your function will return a Promise.

So you want to do this:

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {

    chrome.downloads.search({id: files[i]}, 
      function (item) { 
        sendResponse(item[0].id);
      }
    );

   return true;
});

But really, you want to do this:

chrome.runtime.onMessage.addListener(
  (req, sender, sendResponse) => {
    chrome.downloads.search({id: files[i]}, 
       item => sendResponse(item[0].id);
    );
   return true;
});

Or at least, I do! :-)

For the looping. You want to return an array of files. So what you do in your program is:

  1. Create an array of "A Promise of a file or undefined"
  2. Wait for all those Promises to resolve.
  3. Filter out the undefined.
  4. Return the array of files.
chrome.runtime.onMessage.addListener(
  (req, sender, sendResponse) => {
   if (req !== "files") { return } // why wait?

   Promise.all(files.map(file => 
     new Promise(resolve =>     
       chrome.downloads.search({id: file},
         item => resolve(item[0].exists ? 
         {
            filename: [item[0].filename, 
            id: item[0].id]
          } : undefined)))))
    .then(res => res.filter(i => !!i)) //remove empty
    .then(sendResponse)

  return true;
});

Basically, if you are using loops or variables, you're doing it wrong. Constants and functions only.

Hope this helps.

Flafy
  • 176
  • 1
  • 3
  • 15
Josh Wulf
  • 4,727
  • 2
  • 20
  • 34
  • 1
    Looks about right. – Roamer-1888 Feb 22 '20 at 16:42
  • Thanks for the correction! Though in my code I have the `chrome.downloads.search` inside a for loop. in every loop, inside the downloads.search, it will add an item to an array that will later be sent as a response. How do I do it? Also, some strange things are happening because it's doing the `chrome.downloads.search` parallel. The code: https://pastebin.com/0zmV76fi – Flafy Feb 22 '20 at 17:45
  • Updated it for you. – Josh Wulf Feb 22 '20 at 18:33