Javascript 承诺 - 为什么这些不起作用

Javascript promises - why aren't these working

我对 Promises 功能进行了以下 mocha 测试,但结果令人困惑。

describe('test promises', () => {
    const constPromise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('11');
        }, 500);
    });

    const constPromise2 = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('2222');
        }, 500);
    });

    const functionPromise = (arg) => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(`${arg} 333333`);
            }, 1000);
        })
    }

    const functionPromise2 = (arg) => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve(`${arg} 44444444`);
            }, 1000);
        })
    }

    it('using async', async () => {

        functionPromise(1).then(a => {
            console.log(`function promise (then ): ${a}`)
        })

        const x1 = await functionPromise(2)
        console.log(`function promise (await): ${x1}`)

        functionPromise2(3).then(a => {
            console.log(`function2 promise (then ): ${a}`)
        })

        // const x2 = await promiseFn()
        const x2 = '88 NOT ACTUALLY A PROMISE'
        console.log(`function2 NOT A promise: ${x2}`)

        constPromise.then(b => {
            console.log(`const    promise (then ): ${b}`)
        })

        const a = await constPromise
        console.log(`const    promise (await): ${a}`)

        constPromise2.then(b => {
            console.log(`const2    promise (then): ${b}`)
        })

        // const a2 = await promiseConst2
        const a2 = '99 NOT ACTUALLY A PROMISE'
        console.log(`const2     NOT A promise: ${a2}`)
    })

    it('async iife', () => {
        (async () => {
            functionPromise(3).then(a => {
                console.log(`function promise (then): ${a}`)
            })

            constPromise.then(b => {
                console.log(`const promise (then): ${b}`)
            });

            const x = await functionPromise(4)
            // const x = 'NOT ACTUALLY A PROMISE'// await myPromiseFn()
            console.log(`function promise (await): ${x}`)

            // return myPromise.then(a => console.log(`promise: ${a}`)).catch(e => console.log(`promise error: ${e}`))
            const a = await constPromise
            console.log(`const promise (await): ${a}`)

        })()
    })
})

结果是这样的:

/Users/abba/.nvm/versions/node/v12.22.6/bin/node /Users/abba/dev/_tools/updateUsersData/node_modules/mocha/bin/mocha --ui bdd --reporter /Applications/IntelliJ IDEA.app/Contents/plugins/NodeJS/js/mocha-intellij/lib/mochaIntellijReporter.js /Users/abba/dev/_tools/updateUsersData/test/app-spec2.js --grep ^test promises 

function promise (then ): 1 333333
function promise (await): 2 333333
function2 NOT A promise: 88 NOT ACTUALLY A PROMISE
const    promise (then ): 11
const    promise (await): 11
const2     NOT A promise: 99 NOT ACTUALLY A PROMISE
const2    promise (then): 2222
iife const promise (then): 11

以及 Nodejs v12.22.6 我试过 Nodejs 16.8.0。

让我困惑的注意事项:

  1. 没有function2 promise (then ): 3

但是有function promise (await): 2await 被要求触发 .then() 似乎从 functionPromise 等效测试。这是为什么?

  1. constPromises 似乎不成立 - 尽管没有 await constPromise2,但 const2 promise (then): 2222 还是出现了。与函数中的 Promises 相比,变量中 Promises 的行为为何不同?

  2. async iife 测试有一个结果,但是如果我 运行 只有它而没有其他 NONE。这是为什么?

任何更深入的解释将不胜感激。

好吧,这让我有点困惑,但我想通了。这可能有点令人困惑,所以请随时要求更多说明,但我会尽力而为。 记录什么和不记录什么的关键因素是时间的函数。让我们看看测试在做什么以及它们的结果是什么。

第一次测试 - “使用异步”

functionPromise(1).then(a => {
            console.log(`function promise (then ): ${a}`)
        })

functionPromise returns 一个承诺,但由于我们不等待该承诺(通过说 await functionPromise),代码将继续进行。 .then() 不会阻塞代码流,只会在 promise 被解析后异步调用其中的函数。

const x1 = await functionPromise(2)
        console.log(`function promise (await): ${x1}`)

由于您这次正在等待(通过等待)functionPromise 解析,它将等待 promise 解析(基本上等待一秒钟),然后记录下一行。 但请注意,第一个 promise 调用现在也已经解决(就在这个之前)并且会有 运行 解决,之后下一行执行并记录你的第二个结果。

所以现在的日志看起来像

function promise (then ): 1 333333
function promise (await): 2 333333

好吗?接下来是 functionPromise2

functionPromise2(3).then(a => {
    console.log(`function2 promise (then ): ${a}`)
})

现在虽然功能相同,但这就是它不记录任何内容的原因。正如我们在第一个承诺中看到的那样,此调用也不等待承诺解决,因此不会立即记录任何内容并移至下一行。,即 -

 const x2 = '88 NOT ACTUALLY A PROMISE'
        console.log(`function2 NOT A promise: ${x2}`)

因此被添加到日志中。请记住,将记录 functionPromise2 的日志,但在 1 秒后。

所以现在的日志预计是 -

function promise (then ): 1 333333
function promise (await): 2 333333
function2 NOT A promise: 88 NOT ACTUALLY A PROMISE

正如您所展示的那样。 下一个

 constPromise.then(b => {
            console.log(`const    promise (then ): ${b}`)
        })

再次。不用等待。所以我们继续前进。什么都没有记录。此日志将在 500 毫秒内 运行,因为那是 constPromise 解析的时间。虽然,就在这之后你打电话给

const a = await constPromise
        console.log(`const    promise (await): ${a}`)

等待。这将等待 500 毫秒。这允许之前的承诺也得到解决,我们得到 2 个日志

const    promise (then ): 11
const    promise (await): 11

所以现在登录

function promise (then ): 1 333333
function promise (await): 2 333333
function2 NOT A promise: 88 NOT ACTUALLY A PROMISE
const    promise (then ): 11
const    promise (await): 11

下一个。

constPromise2.then(b => {
            console.log(`const2    promise (then): ${b}`)
        })

由于我们没有等待,因此还没有记录任何内容。代码向前移动。

const a2 = '99 NOT ACTUALLY A PROMISE'
console.log(`const2     NOT A promise: ${a2}`)

过了一会儿, 然后这段代码记录

const2     NOT A promise: 99 NOT ACTUALLY A PROMISE

然后在 500 毫秒后 constPromise2 解析并记录

const2    promise (then): 2222

所以日志实际上符合预期。

现在是“async iife” 由于测试用例不是异步的,所以测试用例会同步 运行 里面的函数。 所有行 运行 和测试退出。

现在出现了一些灰色区域,我不是 100% 确定。

const2    promise (then): 2222
iife const promise (then): 11

这些日志发生在测试用例实际完成之后,因为它们正在等待,但由于测试用例本身可能需要大约 500 毫秒才能结束,因此该日志能够进入控制台。也许更高的超时时间可以证明这一理论。

原因是我使用的是 Mocha v3.5.3(最新的是 v9.1.3)。

所以当我使用 Mocha v3.5.3 并且测试是 运行 从终端我们看到:

> ./node_modules/mocha/bin/mocha --timeout 10000 ./test/app-spec2.js


  test promises
function promise (then ): 1 333333
function promise (await): 2 333333
function2 NOT A promise: 88 NOT ACTUALLY A PROMISE
const    promise (then ): 11
const    promise (await): 11
const2     NOT A promise: 99 NOT ACTUALLY A PROMISE
const2    promise (then): 2222
    ✓ using async (1001ms)
    ✓ async iife
iife const promise (then): 11


  2 passing (1s)

但是当我升级到最新的 Mocha 时,我们看到:

> ./node_modules/mocha/bin/mocha --timeout 10000 ./test/app-spec2.js


  test promises
function promise (then ): 1 333333
function promise (await): 2 333333
function2 NOT A promise: 88 NOT ACTUALLY A PROMISE
const    promise (then ): 11
const    promise (await): 11
const2     NOT A promise: 99 NOT ACTUALLY A PROMISE
const2    promise (then): 2222
    ✔ using async (1004ms)
    ✔ async iife
iife const promise (then): 11


  2 passing (1s)

function2 promise (then ): 3 44444444
iife function promise (then): 3 333333
iife function promise (await): 4 333333
iife const promise (await): 11

所以你可以看到所有的承诺都在解决。但是,您还可以看到 'missing' 的承诺在测试完成后得到解决。

有趣的是,IntelliJ 似乎在输出后 运行 对输出进行分类(我从未见过)——但这是一个单独的问题。

我尝试添加一个 'done' 方法:

   it('using async', async (done) => {
        // console.log(`before promise call outside`)

        functionPromise(1).then(a => {
            console.log(`function promise (then ): ${a}`)
        })

        ...

        done()
        assert.isTrue(true)
    })

但是未对齐仍然存在,我得到一个错误:

Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both

我不明白这是为什么。但是在 Mocha 测试中使用 Promises 是不正常的,我现在不打算继续这样做。我似乎找到了让我继续实际工作的答案。

我不能放过它,需要找出如何解决在测试完成后承诺解决的问题,如问题和我之前的回答 - .

我整理了更多代码来展示如何最好地使用 promises 超时。

describe('test promises', () => {
    const timeout=2500
    let startTime
    beforeEach(() => {
        startTime = new Date()
    })
    afterEach(() => {
        const endTime = new Date()
        const diff = endTime - startTime
        console.log(`          time to run test: ${diff} ms`)
    })
    describe('new tests', () => {
        // Both then and await fail for this
        const failurePromiseFn = (arg) => {
            return new Promise(resolve => {
                setTimeout(
                    resolve(`${arg} - failurePromiseFn`), timeout);
            })
        }

        // Both succeed
        const successPromiseFn = (arg) => {
            return new Promise(resolve => {
                setTimeout(() => {
                    resolve(`${arg} - successPromiseFn`)
                }, timeout);
            })
        }

        describe('these dont work - they finish BEFORE end of test BUT timeout fails', () => {
            it('failurePromiseFn - then', (done) => {
                // order correct, no timeout
                failurePromiseFn(1).then(res => {
                    console.log(`          failurePromiseFn - then: ${res}`)
                    done()
                })
            })
            it('failurePromiseFn - await', async () => {
                // order correct, no timeout
                const res = await failurePromiseFn(2)
                console.log(`          failurePromiseFn - await: ${res}`)
            })
        })

        describe('these work - they finish BEFORE end of test AND timeout succeeds', () => {
            it('successPromiseFn - await', async () => {
                // order correct, yes timeout
                const res = await successPromiseFn(3)
                console.log(`          successPromiseFn - await: ${res}`)
            })

            it('successPromiseFn - then', (done) => {
                // order not correct, yes timeout
                successPromiseFn(4).then(res => {
                    console.log(`          successPromiseFn - then: ${res}`)
                    done()
                })
            })
        })

        describe('other examples from internet', () => {
            const delay = t => new Promise(resolve => setTimeout(resolve, t));

            it('using delay - then - succeeds', (done) => {
                delay(timeout).then(() => {
                    console.log('          Hello')
                    done()
                });
            })

            it('using delay - then - fails as no done so timeout fails', () => {
                delay(timeout).then(() => {
                    console.log('          Hello')
                });
            })

            it('using delay - await - succeeds', async () => {
                await delay(timeout)
                console.log('          Hello')
            })
        })
    })
})

结果是:

  test promises
    new tests
      these dont work - they finish BEFORE end of test BUT timeout fails
          failurePromiseFn - then: 1 - failurePromiseFn
        ✓ failurePromiseFn - then
          time to run test: 3 ms
          failurePromiseFn - await: 2 - failurePromiseFn
        ✓ failurePromiseFn - await
          time to run test: 0 ms
      these work - they finish BEFORE end of test AND timeout succeeds
          successPromiseFn - await: 3 - successPromiseFn
        ✓ successPromiseFn - await (2504ms)
          time to run test: 2504 ms
          successPromiseFn - then: 4 - successPromiseFn
        ✓ successPromiseFn - then (2501ms)
          time to run test: 2501 ms
      other examples from internet
          Hello
        ✓ using delay - then - succeeds (2503ms)
          time to run test: 2503 ms
        ✓ using delay - then - fails as no done so timeout fails
          time to run test: 1 ms
          Hello
          Hello
        ✓ using delay - await - succeeds (2504ms)
          time to run test: 2504 ms


  6 passing (10s)