异步/等待不起作用?

3
我正在尝试编写一个Firefox网页扩展程序。我想要一个返回选项卡URL的函数。
Firefox选项卡browser.tabs.get(id)返回一个Promise,它解析为一个带有URL成员的选项卡对象。 browser.tabs.query返回一个Promise,它解析为一个包含选项卡id的选项卡数组。
计划将上述两个调用包装在一个Promise中,该Promise解析为tab.url。使用async/await等待其解析并返回结果。
根据MDN文档:

"await表达式会导致异步函数执行暂停,直到Promise被履行(即被解析或拒绝),并在履行后恢复异步函数的执行。恢复时,await表达式的值为已履行Promise的值。"

清单文件具有选项卡权限。
因此,我的代码如下:
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)
      })
  })
}

如上所示,控制台的console.dir和console.log语句已经被注释掉了。我调试代码并使用命令行执行测试函数。我错误地认为测试函数中的await应该等待promise解析,然后让我返回值(实际上不是这样的)。
好的,如果我取消注释console.log和console.dir语句,则从命令行运行测试会得到预期的(url)结果。在控制台中,我可以看到log/dir语句的结果,并且当从命令行运行时,最终结果是选项卡的URL(您可以从test2的控制台.dir中获取tab.id)。
如果我注释掉console.dir语句,则日志中会出现一行显示“Promise { : "pending" }”,紧随其后的是新行的期望结果(url)。
如果我然后再注释掉console.log语句,则我只会得到“Promise { : "pending" }”这一行,永远不会得到期望的URL。
如果有人想尝试,请参考下面的清单文件:
{

  "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
避免使用Promise构造函数反模式! - Bergi
如果我取消注释掉console.log和console.dir语句,从命令行运行测试会得到预期的(url)结果。那么你就这样做吧? - Bergi
2
"await" 不会使代码同步,它仍然是一个返回 Promise 的 "async function"。 - Bergi
我发现的第一件事是...你需要返回 browser.tabs.get - Jaromanda X
Bergi,为什么要像“那么就去做吧”这样发表评论呢?这样是没有任何帮助的,即使它能起作用,也可能在其他情境下无法正常工作。解释具体问题所在将会更有帮助。你的第二条评论实际上包含了答案的端倪,但正如我所说,我还是个新手,这并不足够。 - Charles Bisbee
2个回答

2
在文档中发现,异步函数必须返回一个 Promise。任何 return 语句。不包含 Promise 的 return 语句的值将被忽略,如上所示。显然,函数中的最后一个 Promise 被返回。
因此,据我所见,您无法编写等待 Promise 返回值的函数。
使用 let xx = await promise,xx 的值在 await 语句后的同一函数中可用,并且可以在那里使用。因此,您必须使调用函数变为异步,并根据需要使用一系列 await 来获取所需的值,然后在函数内部使用它。

3
在文档中发现异步函数必须返回一个Promise。任何return语句,如果不包含promise,其返回值将被忽略... 如果有这样的文档,那么它是完全错误的。这不是async函数的工作方式。您无需在async函数中显式创建Promise。JavaScript引擎会在幕后为async函数返回的Promise创建Promise。在async函数内部,return 42;会使用值42实现自动创建的Promise。 - T.J. Crowder
他并没有说必须明确创建Promise,而只是函数_返回_一个Promise - 这个Promise可能是隐式创建的。 - WestCoastProjects

2

尝试使用async/await环境,如果有的话,优先使用它而不是使用.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;
}

1
晚了点,但还是要感谢你。我生病了,但现在好多了,欠你一份感谢。 - Charles Bisbee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接