Unexpected behaviour in embedded extension when sending messages in a loop

I am trying to send a message from webextension to SDK inside a loop. I need to send the message and get the response in each iteration before the next iteration. However, the code does iterate over all the loop, then sends the reply from the SDK. I do not know whether it behaves correctly and the message from SDK was sent on time? then, why it did not get reply on the right time as well? how to overcome this problem?

What is the reason the code is not running sequentially? How can I overcome this problem (i.e., do not go to the next iteration until receive a reply from the SDK).

The files structure is:

embedding-extension [directory]
    - index.js
    - package.json
    - webextension [directory]
       - main.js
       - manifest.json

Here is the minimal code:

  1. index.js code:

    const webExtension = require(“sdk/webextension”);
    console.log(“in SDK: inside embedding extension”);

    // Start the embedded webextension
    webExtension.startup().then(api => {
    const {browser} = api;
    browser.runtime.onMessage.addListener((msg, sender, sendReply) => {
    if (msg == “send-to-sdk”) {
    console.log(“in SDK: message from webExt has been received”);
    sendReply({
    content: “reply from SDK”
    }); //end send reply
    }//end if

     }); //end browser.runtime.onMessage
    

    }); //end webExtension.startup

  2. package.json code:

    {
    “title”: “testhybrid”,
    “name”: “testhybrid”,
    “version”: “0.0.1”,
    “description”: “A basic add-on”,
    “main”: “index.js”,
    “author”: “”,
    “engines”: {
    “firefox”: “>=38.0a1”,
    “fennec”: “>=38.0a1”
    },
    “license”: “MIT”,
    “hasEmbeddedWebExtension”: true,
    “keywords”: [
    “jetpack”
    ]
    }

  3. In webextension directory main.js code is:

    var flag=true;
    (function aMethod() {
    console.log(“in webExt: inside aMethod”);
    for(var x=0; x<2; x++)
    {
    console.log("loop iteration: "+x);
    if(flag==true)
    {
    console.log(“inside if”);
    console.log(“will send message to SDK”);
    browser.runtime.sendMessage(“send-to-sdk”).then(reply => {
    if (reply) {
    console.log("in webExt: " + reply.content);
    } //end if
    }); //end browser.runtime.sendMessage

       }//end if flag
    
     else
       {
         console.log("inside else");
       }//end else
    

    }//end for
    })();//end aMethod

  4. In webextension directory, manifest.json code is:

    {
    “manifest_version”: 2,
    “name”: “webExt”,
    “version”: “1.0”,
    “description”: “No description.”,
    “background”: {
    “scripts”: [“main.js”]
    },

    “permissions”: [
    “activeTab”
    ],

    “browser_action”: {
    “default_icon”: {
    “64”: “icons/myicon.png”
    },
    “default_title”: “webExt”
    }
    }

The log I am getting is:

testhybrid:in SDK: inside embedding extension  index.js:2
in webExt: inside aMethod  main.js:3:3
loop iteration: 0  main.js:6:2
inside if  main.js:9:9
will send message to SDK  main.js:10:9
loop iteration: 1  main.js:6:2
inside if  main.js:9:9
will send message to SDK  main.js:10:9
testhybrid:in SDK: message from webExt has been received index.js:9
in webExt: reply from SDK

As you can see, I got reply from SDK at the end of the loop. What Ireally need is to send a message and get a reply in each iteration. I appreciate your help.

If you make aMethod an async function and await the result of browser.runtime.sendMessage instead of adding a Promise handler, it should work

Thanks. I did your suggestion. It worked in solving the sequential execution. But I do not receive reply. It return undefined value.

I only changed the main.js by defining it async and await for the return from the browser.runtime.sendMessage. The code after updates look:

var flag=true;
(async function aMethod() {
  console.log("in webExt: inside aMethod");
  for(var x=0; x<2; x++)
  {
	console.log("loop iteration: "+x);
    if(flag==true)
      {
        console.log("inside if");
        console.log("will send message to SDK");
        var message = browser.runtime.sendMessage("send-to-sdk");
      	  if (await message) {
      		    console.log("in webExt the received reply: " + message.content);
          } //end if
      }//end if flag

    else
      {
        console.log("inside else");
      }//end else
  }//end for
})();//end aMethod 

But the debugging shows undefined in the reply content:

testhybrid:in SDK: inside embedding extension  index.js:2
in webExt: inside aMethod  main.js:3:3
loop iteration: 0  main.js:6:2
inside if  main.js:9:9
will send message to SDK  main.js:10:9
testhybrid:in SDK: message from webExt has been received  index.js:9
in webExt the received reply: undefined  main.js:13:13
loop iteration: 1  main.js:6:2
inside if  main.js:9:9
will send message to SDK  main.js:10:9
testhybrid:in SDK: message from webExt has been received  index.js:9
in webExt the received reply: undefined

Can you please point to me what else I need to change? and whether the changes I made are correct? Thanks again.

Just directly await the return value:

        const reply = browser.runtime.sendMessage("send-to-sdk").then(reply => {
      	console.log("in webExt: " + reply ? reply.content : "<<no response message>>");

That did not change the output of original code in my first post where I get a replay from SDK after the loop finishes. Can you please test it in your side?

I want sequential execution: 1) webextension to send message; 2) wait for response from SDK, 3) contine to the next iteration, where steps 1) 2) repeated.

After adding your last suggestion, this is what I get:

testhybrid:in SDK: inside embedding extension  index.js:2
in webExt: inside aMethod  main.js:3:3
loop iteration: 0  main.js:6:2
inside if  main.js:9:9
will send message to SDK  main.js:10:9
loop iteration: 1  main.js:6:2
inside if  main.js:9:9
will send message to SDK  main.js:10:9
testhybrid:in SDK: message from webExt has been received index.js:9
reply from SDK

As you can see, I only could get reply from SDK after the second iteration.

For your reference, this is the latest main.js after your suggestion:

var flag=true;
(async function aMethod() {
  console.log("in webExt: inside aMethod");
  for(var x=0; x<2; x++)
  {
	console.log("loop iteration: "+x);
    if(flag==true)
      {
        console.log("inside if");
        console.log("will send message to SDK");
        const reply = browser.runtime.sendMessage("send-to-sdk").then(reply => {
      	console.log("in webExt: " + reply ? reply.content : "<<no response message>>");
        });
      }//end if flag

    else
      {
        console.log("inside else");
      }//end else
  }//end for
})();//end aMethod

Oh wow. I forgot the await. It should be:

        const reply = await browser.runtime.sendMessage("send-to-sdk").then(reply =&gt; {
      	console.log("in webExt: " + reply ? reply.content : "&lt;&lt;no response message&gt;&gt;");

Thanks. It seems fine now.

One more question please. Does this mean the execution is sequential? the concepts of async and await are new to me. I just want to make sure that the next iteration will not start until a response from the SDK has been received.

Yes. await pretty much pauses the function until the Promise passed to it resolves (which in your case means until the reply is received).

There is something I observed which I could not explain. Because it is an embedded extesnion, I start it from SDK.

When I run the above code with -p C:\Users\myuser\Documents\mydev\myExt --no-copy, it seems that the extension does not start execution from SDK because the line that I print in the console at the beginning of the SDK console.log("in SDK: inside embedding extension"); does not print to the console. However, when I remove the -p C:\Users\myuser\Documents\mydev\myExt --no-copy, it start from SDK and the line console.log("in SDK: inside embedding extension"); get printed in the console.

Can you explain why? how can i make it always start from SDK?

omitted by the user – incorrect post.

For Firefox to log messages from SDK extensions, you need to set an extension-specific config value. Jst start the extension with jpm once, search for logLevel and you will see what you need to set.


Since Firefox 52 it should be possible to use an async function as the browser.webRequest.onBeforeRequest listener. (And Firefox should await its return value before proceeding with the request.)

Can I run an embedding extension from normal firefox (non-developer) using about:debugging? I can not see the SDK log when using about:debugging.

I did searched for logLevel key and found these:

Which one will allow me t see the SDK console messages? hat value I should set?

I’d say the bold one looks promising. You will need to create it (that’s why I said you should run from jpm when searching for it). Set it’s value to all.