未处理的 promises 错误在 promises 数组内部发生。

huangapple go评论77阅读模式
英文:

Unhandled promises error inside array of promises

问题

我有这段代码。我发出一个writeRecords请求,然后再次执行相同操作。
下一个请求我将其放入一个数组中,然后使用Promise.all()

我的问题是:

如果requests.push(writeClient.writeRecords(timestreamParams).promise())出现错误,它会是一个未处理的Promise错误,还是try / catch会捕获错误?
因为它在一个循环中,可能第一个数组元素已经发送了响应,而其他数据仍然在循环中,所以我不确定错误会发生在哪里?

let requests = []
const chunkSize = 100
for (let i = 0; i < Records.length; i += chunkSize) {
  const chunk = Records.slice(i, i + chunkSize)
  const timestreamParams = {
    DatabaseName: db_name,
    TableName: TimeSpanRawE[table_name],
    Records: chunk,
  }
  await writeClient
    .writeRecords(timestreamParams)
    .promise()
    .catch(() => {}) // 静默处理错误

  // 成功重试相同的writeRecordsRequest,使用相同的记录和版本,因为writeRecords API是幂等的。

  requests.push(writeClient.writeRecords(timestreamParams).promise())
}

try {
  return Promise.all(requests).then((res) => ok(res))
} catch (error) {
  return err({ error, params })
}
英文:

I have this piece of code. I make an writeRecords request and then I do it again.
The next request I put in an array and use an Promise.all()

My question is:

What if requests.push(writeClient.writeRecords(timestreamParams).promise()) has an error, will it be an unhandled Promise error, or will the try / catch catch the error?
Because it's in an loop it can be that the first element in the array has already sent back an response while the other data is still in the loop so I am not sure where the error will occur?

let requests = []
  const chunkSize = 100
  for (let i = 0; i &lt; Records.length; i += chunkSize) {
    const chunk = Records.slice(i, i + chunkSize)
    const timestreamParams = {
      DatabaseName: db_name,
      TableName: TimeSpanRawE[table_name],
      Records: chunk,
    }
    await writeClient
      .writeRecords(timestreamParams)
      .promise()
      .catch(() =&gt; {}) // Silent error

    // Successfully retry same writeRecordsRequest with same records and versions, because writeRecords API is idempotent.

    requests.push(writeClient.writeRecords(timestreamParams).promise())
  }

  try {
    return Promise.all(requests).then((res) =&gt; ok(res))
  } catch (error) {
    return err({ error, params })
  }

答案1

得分: 1

是的,如果在达到Promise.all之前(因为循环在await上暂停)requests数组中的一个promise被拒绝,你的代码可能会导致未处理的promise拒绝。如果循环以同步方式运行,就不会发生这种情况。

你应该使用以下两种方式之一:

并发版本:

try {
  const requests = []
  const chunkSize = 100
  for (let i = 0; i < Records.length; i += chunkSize) {
    const chunk = Records.slice(i, i + chunkSize)
    const timestreamParams = {
      DatabaseName: db_name,
      TableName: TimeSpanRawE[table_name],
      Records: chunk,
    }
    requests.push(writeClient.writeRecords(timestreamParams).promise())
  }

  const res = await Promise.all(requests)
  ok(res)
} catch (error) {
  return err({ error, params })
}

或者顺序版本:

try {
  const res = []
  const chunkSize = 100
  for (let i = 0; i < Records.length; i += chunkSize) {
    const chunk = Records.slice(i, i + chunkSize)
    const timestreamParams = {
      DatabaseName: db_name,
      TableName: TimeSpanRawE[table_name],
      Records: chunk,
    }
    res.push(await writeClient.writeRecords(timestreamParams).promise())
  }

  ok(res)
} catch (error) {
  return err({ error, params })
}

但不要混合它们。如果你真的需要的话,.catch(() => {}) // Silence error应该放在你立即await的promise上(也就是你放在数组中的那些promise)。

英文:

Yes, your code can lead to unhandled promise rejections, if one of the promises in the requests array rejects before the Promise.all is reached (due to the loop being suspended on the await). It cannot happen if the loop were run synchronously.

You should be using either the concurrent version

try {
  const requests = []
  const chunkSize = 100
  for (let i = 0; i &lt; Records.length; i += chunkSize) {
    const chunk = Records.slice(i, i + chunkSize)
    const timestreamParams = {
      DatabaseName: db_name,
      TableName: TimeSpanRawE[table_name],
      Records: chunk,
    }
    requests.push(writeClient.writeRecords(timestreamParams).promise())
  }

  const res = await Promise.all(requests)
  ok(res)
} catch (error) {
  return err({ error, params })
}

or the sequential version

try {
  const res = []
  const chunkSize = 100
  for (let i = 0; i &lt; Records.length; i += chunkSize) {
    const chunk = Records.slice(i, i + chunkSize)
    const timestreamParams = {
      DatabaseName: db_name,
      TableName: TimeSpanRawE[table_name],
      Records: chunk,
    }
    res.push(await writeClient.writeRecords(timestreamParams).promise())
  }

  ok(res)
} catch (error) {
  return err({ error, params })
}

but not the mix between them. If you really needed to, the .catch(() =&gt; {}) // Silence error needs to go on the promise that you are not awaiting immediately (i.e. the ones you put in the array).

答案2

得分: 0

我会将重试逻辑放在一个单独的函数中,以便分离关注点。我的通常做法是将其添加到 Promise 中:Promise.retry

另外,您的请求彼此独立,因此可以并行执行它们。我们使用 Array::map() 来获得一个 Promise 数组。

async function retry(func, numberOfRetries = 1) {
    let error;
    do {
        try {
            return await func();
        } catch (e) {
            // 这里可以添加一些错误处理逻辑
            error = e;
        }
    } while (--numberOfRetries);
    throw error;
}

const chunkSize = 100;

const requests = Records.reduce((chunks, item, index) =>
    ((chunks[Math.floor(index / chunkSize)] ??= []).push(item)) && chunks
    , [])
    .map(chunk => retry(writeRecords({
        DatabaseName: db_name,
        TableName: TimeSpanRawE[table_name],
        Records: chunk,
    }).promise()));

try {
    return Promise.all(requests).then((res) => ok(res))
} catch (error) {
    return err({ error, params })
}

希望这可以帮助您。如果您有任何问题,请随时提出。

英文:

I would put the retry logic in a separate function to make some separation of concerns. My usual way is to add to the Promise: Promise.retry.

Also your requests are independent from each other so you could execute them in parallel. We use Array::map() for that to get an array of promices

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

async function retry(func, numberOfRetries = 1) {
    let error;
    do {
        try {
            return await func();
        } catch (e) {
            // some error logic would be nice
            error = e;
        }
    } while (--numberOfRetries);
    throw error;
}

const chunkSize = 100;

const requests = Records.reduce((chunks, item, index) =&gt;
    ((chunks[Math.floor(index / chunkSize)] ??= []).push(item)) &amp;&amp; chunks
    , [])
    .map(chunk =&gt; retry(writeRecords({
        DatabaseName: db_name,
        TableName: TimeSpanRawE[table_name],
        Records: chunk,
    }).promise()));

try {
    return Promise.all(requests).then((res) =&gt; ok(res))
} catch (error) {
    return err({ error, params })
}

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年6月15日 21:38:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76483079.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定