简体   繁体   English

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

[英]Javascript promises - why aren't these working

I have the following mocha test of Promises functionality but the results are confusing.我对 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}`)

        })()
    })
})

The results are this:结果是这样的:

/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

Aswell as Nodejs v12.22.6 i tried Nodejs 16.8.0.除了 Nodejs v12.22.6,我还尝试了 Nodejs 16.8.0。

Things to note that confuse me:让我感到困惑的注意事项:

  1. There is NO function2 promise (then ): 3没有function2 promise (then ): 3

But there is function promise (await): 2 .但是有function promise (await): 2 The await was required to trigger the .then() it seems from the functionPromise equivalent tests.functionPromise等效测试看来, await是触发.then()所必需的。 Why is this?为什么是这样?

  1. The same does not seem to hold for the constPromises - const2 promise (then): 2222 came out despite there being no await constPromise2 . constPromises - const2 promise (then): 2222尽管没有await constPromise2 ,但还是出现了 2222 。 Why is the behaviour of Promises in variables compared to Promises from function differ?为什么 Promises 在变量中的行为与来自 function 的 Promises 不同?

  2. There is ONE result from async iife test BUT if i run it alone without the other there are NONE. async iife测试有一个结果,但是如果我单独运行它而没有另一个结果,则没有。 Why is this?为什么是这样?

Any deeper explanation would be appreciated.任何更深入的解释将不胜感激。

Ok so this confused me a bit but I figured it out.好的,所以这让我有点困惑,但我想通了。 It might be a little confusing so feel free to ask for more clarification but I'll try my best.这可能有点令人困惑,所以请随时要求更多澄清,但我会尽力而为。 The key factor of what's getting logged and what's not is a function of time.记录什么和不记录什么的关键因素是时间的 function。 Let us see what the tests are doing and what's their result.让我们看看测试在做什么以及它们的结果是什么。

First test - "using async"第一个测试 - “使用异步”

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

functionPromise returns a promise but since we are not awaiting that promise (by saying await functionPromise) the code will move forward. functionPromise 返回 promise 但由于我们不等待 promise(通过说 await functionPromise)代码将继续前进。 .then() does not block the flow of code but only calls the function inside it after the promise is resolved, asynchronously. .then() 不会阻塞代码流,而只会在 promise 被解析后异步调用其中的 function。

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

Since you are awaiting (by putting await) for the functionPromise to resolve this time, it will wait for the promise to resolve (basically wait a second) and then log the next line.由于您正在等待(通过等待) functionPromise 这次解决,它将等待 promise 解决(基本上等待一秒钟)然后记录下一行。 But note, the first promise call would have also been resolved by now (just before this one) and would have run the resolve, after which the next line executes and logs your second result.但请注意,第一个 promise 调用现在也已解决(就在此之前)并且会运行解析,然后下一行执行并记录您的第二个结果。

So the log as of now looks like所以现在的日志看起来像

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

Good?好的? Next up is functionPromise2接下来是functionPromise2

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

Now although the functions are identical, here's why it doesnt log anything.现在虽然功能相同,但这就是它不记录任何内容的原因。 As we saw for the first promise, this call also does not wait for the promise to resolve and hence does not log anything immediately and moves to the next line., which is -正如我们在第一个 promise 中看到的那样,此调用也不会等待 promise 解决,因此不会立即记录任何内容并移至下一行。,即 -

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

and hence that gets added to the log.因此它被添加到日志中。 Remember, the log for functionPromise2 will be logged, but after 1 second.请记住,functionPromise2 的日志将被记录,但在 1 秒后。

So the log as of now is expected to be -因此,截至目前的日志预计为 -

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

as you've shown as well.正如你所展示的那样。 Next下一个

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

Again.再次。 No await.没有等待。 So we move on.所以我们继续前进。 Nothing gets logged.没有任何记录。 This log will run in 500 ms as that's when constPromise resolves.该日志将在 500 毫秒内运行,因为那是 constPromise 解决的时间。 Although, just after this you call虽然,就在这之后你打电话

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

with await.等待。 This waits for 500 ms.这将等待 500 毫秒。 This allows the previous promise to resolve as well and we get 2 logs这也允许之前的 promise 解决,我们得到 2 个日志

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

So log as of now所以从现在开始登录

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

Next.下一个。

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

since we're not awaiting, this doesnt log anything, yet.因为我们没有等待,所以这还没有记录任何内容。 Code moves forward.代码向前移动。

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

After a while, Then this code logs过了一会儿,然后这段代码记录了

const2     NOT A promise: 99 NOT ACTUALLY A PROMISE

Then after 500ms constPromise2 resolves and logs然后在 500 毫秒后 constPromise2 解析并记录

const2    promise (then): 2222

So the logs are actually as expected.所以日志实际上是预期的。

Now for "async iife" Since the test case is not async, the test case would synchronously run the function inside.现在对于“async iife”,由于测试用例不是异步的,因此测试用例将在内部同步运行 function。 All the lines run and the test exits.所有行都运行并且测试退出。

Now comes a bit of gray area which i'm not 100% sure of.现在出现了一些我不能 100% 确定的灰色区域。

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

These logs happen after the test case has actually finishes as they were awaited on, but since the test cases themselves probably take about 500 ms to wrap up, this log is able to make its way to the console.这些日志发生在测试用例实际完成后,因为它们被等待,但由于测试用例本身可能需要大约 500 毫秒才能结束,因此该日志能够进入控制台。 Maybe a higher timeout for these would prove this theory.也许更高的超时时间可以证明这个理论。

The reason is I was using Mocha v3.5.3 (the latest is v9.1.3).原因是我使用的是 Mocha v3.5.3(最新的是 v9.1.3)。

So when I was using Mocha v3.5.3 and the tests are run from the terminal we see:因此,当我使用 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)

But when I upgrade to the latest Mocha we see:但是当我升级到最新的 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

So you can see all promises are resolving.所以你可以看到所有的承诺都在解决。 However you can also see that the promises that were 'missing' are resolving AFTER the test completes.但是,您还可以看到“缺失”的承诺在测试完成后得到解决。

Interestingly IntelliJ seems to truncate the output after the output (and I never see it) - but this is a separate problem.有趣的是,IntelliJ 似乎在 output 之后截断了 output (我从未见过) - 但这是一个单独的问题。

I tried adding a 'done' method:我尝试添加一个“完成”方法:

   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)
    })

But the mis-alignment still exists and i get an error:但错位仍然存在,我得到一个错误:

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

I'm not why this is.我不是为什么会这样。 But this use of Promises in Mocha tests is not normal and I'm not going to pursue this right now.但是在 Mocha 测试中使用 Promises 是不正常的,我现在不打算这样做。 I seem to have the answer to keep me moving forward with what I was actually working on.我似乎有答案,可以让我继续我实际正在做的事情。

I couldn't let this go and needed to find out how to solve the problem where the promise resolves after the test completes as seen in the question and my previous answer - https://stackoverflow.com/a/70320537/1019307 . I couldn't let this go and needed to find out how to solve the problem where the promise resolves after the test completes as seen in the question and my previous answer - https://stackoverflow.com/a/70320537/1019307 .

I put together some more code showing how best to use timeouts with 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')
            })
        })
    })
})

The results are:结果是:

  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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM