【问题标题】:Timed promise resolve returning immediately after having been resolved with time once beforeTimed Promise resolve 前一次用 time 解决后立即返回
【发布时间】:2019-11-25 19:30:54
【问题描述】:

我在这里的措辞上有点挣扎,但要点是我使用了一个返回对象的 Promise.race(如下所示)。大多数时候,至少有几个 Promise 会崩溃,但这是故意的。这只是意味着没有找到产品。为了避免 promise 返回空白,我使用 try catch 块来启动 15 秒的新定时 promise。这可以防止 Promise 返回空白,让正确完成的最快函数将其对象返回到 Promise.race。在使用 Windows 10 在 NodeJS 10 中进行测试时,这似乎工作得很好,但是当我将它移植到运行 NodeJS 8 和 Ubuntu 18.04 的 Linux 服务器时,我得到了一些奇怪的行为。 Promise.race 工作得非常好,直到我在启动 NodeJS 应用程序后第一次测试该功能以来已经过去了 15 秒。当这 15 秒过去后,当我尝试与正常承诺竞争时,它会立即返回定时承诺。

我首先要兑现这些承诺。 (不确定是否重要,但这是通过 HTTP 请求调用的)

let product = await Promise.race([
        get_info_meny_joker(bar_code, "meny.no"),
        get_info_meny_joker(bar_code, "joker.no"),
        get_info_openfoodfacts(bar_code)
    ])

其中一个看起来像这样

async function get_info_meny_joker(bar_code, link) {

    try {
        let url = 'https://' + link + '/Sok/?query=' + bar_code

        let browser = await puppeteer.launch({args: ['--no-sandbox']})
        let page = await browser.newPage()

        await page.goto(url, { waitUntil: 'networkidle2' })

        let get_link = await page.evaluate(() => document.querySelector('.ws-product__title').getAttribute('href') )
        let product_name = await page.evaluate(() => document.querySelector('.ws-product__title').innerText )
let product_amount = await page.evaluate(() => document.querySelector('.ws-product__subtitle').innerText )
        let regex = "[0-9]+([gl]+|ml| [gl] | ml |kg| kg)"
        let match = product_amount.match(regex)
        match = match[0]
        /*let regex_index = new RegExp("[A-Za-z]")
        let index_match = regex_index.exec(match).index
        match = match.splice(index_match, 0, " ")*/
        product_amount = match

        await page.goto('https://' + link + '/' + get_link, { waitUntil: 'networkidle2' })

        const [first_product, second_product] = await page.$$('.ws-collapsable-block__heading');

        await page.screenshot({ path: "x.png" })

        second_product.click()

        await page.screenshot({ path: "y.png" })

        let img_url = await page.evaluate(() => document.querySelector('.lazyloaded').attributes[1].value)

        // Get a list of all the nutrients found on the page
        let nutrients_raw = await page.evaluate(() => {
            let nutrients_raw = document.querySelector('.ws-nutritional-content').children
            let nutrients = {}
            let i = 0
            for (let item of nutrients_raw) {
                nutrients[i + ""] = item.innerText
                i++
            }
            return nutrients
        });

        // Pretify the nutrients_raw to a nutrients object
        let nutrients = {}
        for (let i = 0; i < Object.size(nutrients_raw); i++) {
            let s = nutrients_raw[i]
            let type = s.slice(0, s.indexOf(':'))
            let amount = s.slice(s.indexOf(':') + 2, s.length)
            nutrients[type] = amount
        }

        return new Product(bar_code, product_name, product_amount, img_url, nutrients, link)

    } catch (err) {
        return await promise
    }

}

定时承诺是这样的

let promise = new Promise((resolve, reject) => {
    let product = new Product()
    product.name = "Could not be located"
    setTimeout(() => resolve(product), 15000)
});

重复我自己,我可以 Promise.race 并且它工作得非常好,直到我第一次参加比赛以来已经过去了 15 秒。

(对这个问题并不重要,但我将 Promise.race 函数理解为从堆栈中完全删除丢失的 Promise。奇怪的是它记得 15 秒过去了。)

【问题讨论】:

    标签: javascript node.js promise


    【解决方案1】:

    您需要在此创建一个新的promise

    let promise = new Promise((resolve, reject) => {
        let product = new Product()
        product.name = "Could not be located"
        setTimeout(() => resolve(product), 15000)
    });
    

    每次您想使用它。否则,当您开始使用它时,计时器已经运行了一段时间,并且它会在远低于 15000 毫秒的时间内触发,因为它已经运行了一段时间。事实上,如果自您创建 Promise 以来已经超过 15000 毫秒,那么它已经被解决,当您在 .race() 中使用它时,比赛将立即以这个已经解决的 Promise 结束。

    这个问题并不重要,但我理解 Promise.race 函数是从堆栈中完全删除丢失的承诺。奇怪的是它记得 15 秒过去了。

    这是不正确的。 Promise.race() 不会删除任何内容。任何输掉比赛的承诺都会继续进行下去。只是Promise.race() 返回的承诺将在比赛中的第一个承诺完成后立即解决。其他人继续顺利,将按照自己的时间表完成。


    您可能想要做的是将它放入一个函数并调用该函数,只要您想要一个 15000 毫秒的承诺:

    function timeoutPromise(t) {
        return new Promise(resolve => {
            let product = new Product();
            product.name = "Could not be located";
            setTimeout(() => resolve(product), t);
        });
    }
    

    然后,当您想使用新的超时承诺时,您只需调用此函数来获取新的承诺,而不是使用已保存的 promise 变量。

    【讨论】:

      猜你喜欢
      • 2022-10-05
      • 2020-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-07
      • 1970-01-01
      • 2018-11-16
      • 2019-05-28
      相关资源
      最近更新 更多