为什么 Promise.all 在非承诺抛出错误时不拒绝?

Why Promise.all doesn't reject when a non-promise throws an error?

根据 MDN,

The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will resolve when all of the input's promises have resolved, or if the input iterable contains no promises. It rejects immediately upon any of the input promises rejecting or non-promises throwing an error, and will reject with this first rejection message / error.

这是一个代码片段,它没有像我预期的那样按照上面的定义捕获错误:-

const promise1 = Promise.resolve(3);
const promise2 = () => {throw new Error('random error')};
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2(), promise3]).then((values) => {
  console.log(values);
}).catch(e=>console.log('Error caught',e));

我知道如果我将 promise2() 转换为 return 拒绝的 Promise 那么它将起作用。但是 non-promises 行呢?它不正确还是我遗漏了什么?

更新 - 我得到了这个行为的答案。只是好奇 non-promises 根据定义抛出错误的可能场景是什么?

因为在你调用Promise.all之前抛出错误。 Promise.all 不可能为您将其转换为拒绝。

您在构建数组时调用 promise2,这发生在您将该数组传递给 Promise.all 之前。

您的代码:

Promise.all([promise1, promise2(), promise3])/*...*/

...等同于以下内容(变量除外):

const x0 = promise1;
const x1 = promise2(); // <==== Error is thrown here
const x2 = promise3;
const array = [x0, x1, x2];
Promise.all(array)/*...*/

要使其按您的意愿工作,您有多种选择:

您可以将其设为 async 函数,如评论中的 所示:

const promise1 = Promise.resolve(3);
const promise2 = async () => {throw new Error('random error')};
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2(), promise3]).then((values) => {
  console.log(values);
}).catch(e=>console.log('Error caught',e));

这是有效的,因为 async 函数总是 return 承诺(即使你不使用 await)并且异步函数中的错误拒绝它的承诺 returns.

您可以将 promise2 设为 thenable,这样在调用 then 之前它不会抛出,然后 Promise.all 会为您将其转换为拒绝:

const promise1 = Promise.resolve(3);
const promise2 = {
    then() {
        throw new Error('random error');
    }
};
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

// Note no () on promise2 below
Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
}).catch(e=>console.log('Error caught',e));

JS 首先尝试获取正确的参数 - 然后将参数传递给被调用的函数。在您的场景中,甚至在调用 Promise.all 之前就会抛出错误。

为了更好地了解情况并认识到 Promise 接口与您的案例无关,请参阅随附的代码片段。使用问题中包含的 3 个参数调用 PromiseAllTest 函数。但是你可以看到这个函数甚至没有被调用。错误在它之前抛出。

const promise1 = Promise.resolve(3);
const promise2 = () => {throw new Error('random error')};
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});


try{
  console.log('Preparing to call Promise Test');
  PromiseAllTest([promise1, promise2(), promise3]);  
  console.log('Promise Test is called successfully');
}
catch(e) {
  console.log('Promise Test is not even called')
}

function PromiseAllTest(promiseArray) {
  console.log('Start Processing');
  console.log(promiseArray);
  console.og('End Processing');
}