简体   繁体   English

Promise 数组和异步函数的问题

[英]Troubles with promise array and async functions

I was trying to implement the Promise.all function using an async function.我试图使用异步函数来实现 Promise.all 函数。 But I get an error when one of the promises is rejected.但是当其中一个承诺被拒绝时,我得到一个错误。 This is the implementation I did:这是我所做的实现:

    async function Promise_All_II(promises){
        let results = []
        for(let p of promises)
            results.push(await p)
        return results
    }

    const takeTime = (value, time=1000) => new Promise(resolve => setTimeout(resolve, time, value))

    let promises = [
        Promise.resolve(1),
        takeTime(2),
        Promise.reject('You are fairly dump'),
        takeTime(3)
    ]
    
    console.log('Promise all II:')
    Promise_All_II(promises).then(console.log, console.log)

And this is the error I get when one of the promises is rejected:这是当其中一个承诺被拒绝时我得到的错误:

Promise all II:
(node:9399) UnhandledPromiseRejectionWarning: You are fairly dump
(node:9399) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:9399) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
You are fairly dump
(node:9399) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

I tried to wrap the for loop with a try-catch but it didn't work.我试图用 try-catch 包装for 循环,但它没有用。 I also tried to place and .catch after p but still didn't work.我还尝试在p之后放置和.catch ,但仍然没有用。

I noticed that the same error occurs when you try to do something like this:我注意到当您尝试执行以下操作时会发生相同的错误:

let f = async () => {
    let ps = [
        Promise.resolve(10),
        Promise.reject(10),
        Promise.resolve(11),
        Promise.resolve(12)
    ]
    for(let p of ps)
        await p
}

f().catch(v => 
    console.log('something happened')
)

Can someone help me please?有人能帮助我吗?

In nodejs, you will get an error about an unhandled rejection if you leave a rejected promise sitting somewhere without a .catch() handler on it when the interpreter gets back to the event loop.在 nodejs 中,当解释器返回事件循环时,如果您将被拒绝的 Promise 留在没有.catch()处理程序的地方,您将收到有关未处理拒绝的错误。 That means you can't just leave a rejected promise in a variable while sitting at an await of some other promise.这意味着您不能在await某个其他承诺时将被拒绝的承诺留在变量中。 This will trigger an unhandled rejection error.这将触发未处理的拒绝错误。 The JS interpreter isn't smart enough to know that you will LATER catch the rejection. JS 解释器不够聪明,无法知道您稍后会收到拒绝。

So, when you do this:所以,当你这样做时:

results.push(await p)

And, any of the other promises reject, you will get the unhandled rejection because that rejected promise has no handler on it for a rejection at the time it rejects and the await causes the interpreter to go back to the event loop.并且,任何其他 Promise 被拒绝,您将得到未处理的拒绝,因为被拒绝的 Promise 在它拒绝时没有处理程序来处理拒绝,并且await会导致解释器返回事件循环。 The interpreter sees you're at idle, a promise has rejected and it has no rejection handler, thus triggering the error.解释器看到你处于空闲状态,一个承诺被拒绝并且它没有拒绝处理程序,从而触发错误。

So, because of the way the unhandled rejections work in nodejs, there really is no way to implement this the way you are trying to do using await and a for loop because this leaves the other promises sitting there with no reject handler during the await .因此,由于未处理的拒绝在 nodejs 中的工作方式,实际上没有办法像您尝试使用awaitfor循环那样实现这一点,因为这使得其他承诺在await期间没有拒绝处理程序。

The usual implementation of Promise.all() involves creating your own promise and using a counter to keep track of when everything has resolved. Promise.all()的通常实现包括创建您自己的 Promise 并使用计数器来跟踪所有问题何时解决。 And, you immediately install both resolve and reject handlers on each promise so they are never in that state that causes the unhandled rejection handler.而且,您会立即在每个 Promise 上安装 resolve 和 reject 处理程序,这样它们就不会处于导致未处理的拒绝处理程序的状态。

Here's a simple implementation of Promise.all() which you can run in this snippet:这是Promise.all()的简单实现,您可以在此代码段中运行它:

 function Promise_All_II(promises) { return new Promise((resolve, reject) => { let cntr = 0; let length = 0; let results; for (let p of promises) { // keep track of index, for later use with result let i = length++; Promise.resolve(p).then(val => { results[i] = val; ++cntr; if (cntr === length) { resolve(results); } }).catch(reject); } if (length === 0) { resolve([]); return; } results = new Array(length); }); } const takeTime = (value, time=1000) => new Promise(resolve => setTimeout(resolve, time, value)) let promises = [ Promise.resolve(1), takeTime(2), Promise.reject('You are fairly dump'), takeTime(3) ]; console.log('Promise all II:') Promise_All_II(promises).then(console.log, console.log);

This immediately installs both a resolve and reject handler on each promise, thus avoiding the unhandled rejection error.这会立即在每个 Promise 上安装 resolve 和 reject 处理程序,从而避免未处理的拒绝错误。

Note, the caller of Promise_All_II() must also have a reject handler.注意, Promise_All_II()的调用者还必须有一个拒绝处理程序。


A few details to note:需要注意的几个细节:

  1. This works on any iterable, not just an array as per the Promise.all() spec.这适用于任何可迭代的,而不仅仅是按照Promise.all()规范的数组。 It doesn't even have to have a .length property.它甚至不必具有.length属性。
  2. This implements "fast reject" (eg reject as soon as any promise rejects) per the Promise.all() spec.根据Promise.all()规范,这实现了“快速拒绝”(例如,一旦任何承诺拒绝就拒绝)。
  3. This allows non-promise values to be in the iterable as per the Promise.all() spec.根据Promise.all()规范,这允许非承诺值在可迭代中。 That's why we use Promise.resolve(p).then(...) so any plain value gets wrapped and we can then treat it as a promise.这就是我们使用Promise.resolve(p).then(...)的原因,因此任何普通值都会被包装,然后我们可以将其视为承诺。

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

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