3
votes

I am trying to write a Foxfire web extension. I want a function that returns the URL of a tab.

Firefox tabs browser.tabs.get(id) returns a promise that resolves to a tab object that has a member with the URL. browser.tabs.query returns a promise that resolves to a tabs array which contains the id of the tabs.

Plan wrap the two calls above in a promise that resolves to the tab.url Use async/await to wait for it to resolve and return the result.

According to the MDN documentation:

"The await expression causes async function execution to pause until a Promise is fulfilled, that is resolved or rejected, and to resume execution of the async function after fulfillment. When resumed, the value of the await expression is that of the fulfilled Promise."

The manifest file has the tabs permission.

So my code is below:

browser.browserAction.onClicked.addListener(test);

async function test(){
  let res = await GetActiveTabURL();
  //console.log(`test ${res}`);
  return res;
}

async function test2(TabID){
  let res = await GetTabURL(TabID);
  //console.log(`test2 ${res}`);
  return res;
}

function GetActiveTabURL() {
  return new Promise((resolve, reject) => {
    browser.tabs.query({currentWindow: true, active: true})
      .then((tabs) => {
        browser.tabs.get(tabs.pop().id)
          .then((tab) => {
            //console.dir(tab);
            resolve(tab.url)
          })
      })
  })
}


function GetTabURL(TabID) {
  return new Promise((resolve, reject) => {
    browser.tabs.get(TabID)
      .then((tab) => {
        //console.dir(tab);
        resolve(tab.url)
      })
  })
}

As shown the console.dir and console.log statements are commented out. I debug the code and use the command line to execute the test functions. I (and I am apparently wrong) think the await in the test functions should wait until the promise resolves and then let me return the values.

Ok if I uncomment the console.log and console.dir statements, running the test from the command line gives the expected (url)result. In the console I see the results of the log/dir statements and, when run from the command line the final result is the URL of the tab (you can get the tab.id from the console.dir for test2).

If I comment out the console.dir statements I get a line in the log that says, "Promise { : "pending" }" followed immediately by the desired result (the url) on a new line.

if I then comment out the console.log statements as well, I ONLY get the "Promise { : "pending" }" line. I NEVER get the desired URL.

the manifest file follows if anyone wants to try this:

{

  "manifest_version": 2,
  "name": "TestGetURL",
  "description": "Functions returning the url",
  "version": "1.0",
  "icons": {
    "48": "icons/link-48.png"
  },

  "permissions": [
    "tabs",
    "contextMenus",
    "nativeMessaging",
    "<all_urls>",
    "webRequest",
    "downloads",
    "clipboardWrite",
    "webNavigation",
    "notifications",
    "storage",
    "cookies",
    "alarms"
  ],

  "background": {
    "scripts": ["background-script.js"]
  },


  "browser_action": {
    "browser_style":false,
    "default_icon": {
      "16": "icons/page-16.png",
      "32": "icons/page-32.png"
    }
  },


  "default_locale": "en"
}
2
"If I uncomment the console.log and console.dir statments, running the test from the command line gives the expected (url)result." - so then just do that?Bergi
An await does not make the code synchronous, it is still an async function that will return a promise.Bergi
first thing I spotted ... you'll want to return browser.tabs.getJaromanda X
Bergi, why make comments such as "so then just do it". They are of no help. Even if it works something is wrong and it is likely not to work in some other context. Explaining what is wrong would be helpful. Your second one actually contains the germ of the answer but, as I said, i am new so it's not enough.Charles Bisbee

2 Answers

2
votes

Found in documentation that async function MUST return a promise. Any return statement. The value of a return statement that does not contain a promise is ignored as shown above. Apparently the last promise in the function is returned.

So, as far as I can see, you cannot write a function that waits for a promise returns the value from that promise.

the value from let xx = await promise, xx is available in the same function after the await statement and can be used there. So rather than calling a function to asynchronously return a value, you must make the calling function async and use a series of awaits as necessary to get to the value you want, then use it within the function.

1
votes

Try this, remember if you have the environment to use async/await then favor that overusing .then().

browser.browserAction.onClicked.addListener(test);

async function test() {
    let res = await GetActiveTabURL();
    return res;
}

async function test2(TabID) {
    let res = await GetTabURL(TabID);
    return res;
}

async function GetActiveTabURL() {
    const tabs = await browser.tabs.query({
        currentWindow: true,
        active: true
    });

    const tab = browser.tabs.get(tabs.pop().id);

    return tab.url;
}

async function GetTabURL() {
    const tab = await browser.tabs.get(TabID);

    return tab.url;
}