即使已经被后续处理程序捕获,Promise拒绝仍会引发警告。

3

示例

class Foo {
    private pro = new Promise(() => {
                      throw new Error();
                  });

    public usePro() {
        return this.pro.then(() => {});
    }
}

let foo = new Foo();
setTimeout(() => {
    foo.usePro().then(() => {
        console.log("end.");
    }).catch(() => {
        console.log("error.");
    })
}, 1000);

我理解JavaScript无法在运行时知道后面是否会有人捕获错误,那么在这种情况下我应该怎么做呢? 控制台
(node:39166) UnhandledPromiseRejectionWarning: error
(node:39166) 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(). (rejection id: 1)
(node:39166) [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.
error.
(node:39166) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

你如何确保以后有人会捕获错误?另外,在构造函数中不应该执行异步操作。 - Bergi
3个回答

1

CertainPerformance 给出了很好的答案。

另外,你还可以在 Node.js 中在 process 上添加一个 unhandledRejection 监听器:

process.on('unhandledRejection', reason => {
    console.error({Error:reason})
    process.exit(1);
});

你不觉得忽略所有警告是很危险的吗?这些警告可能来自其他地方。 - Neok
我并不是说要忽略所有警告,而是要注意unhandledRejections,这可能来自未知的来源。一旦你捕获到它们,当然你应该开始重构你的代码。我只是将上述答案添加为错误处理的良好实践,特别是在开发过程中。 - wlh

1

无论 Promise 被谁返回(并被捕获),都应该在使用 Promise 的地方捕获错误。一种选择是根据原始 Promise 是否解决或拒绝,将 this.proValue 分配为一个 resolved 对象或一个 rejected 对象。然后,在调用 usePro 时,检查 this.proValue 并返回 Promise.resolve(resolved)Promise.reject(rejected)。使用标准的 Javascript,因此可以显示可运行的片段:

class Foo {
  constructor() {
    this.pro = new Promise(() => {
      throw new Error('Problem!');
    })
    .then((resolved) => {
      this.proValue = { resolved };
    })
    .catch((rejected) => {
      this.proValue = { rejected };
    });
  }

  usePro() {
    const { resolved, rejected } = this.proValue;
    if (resolved) return Promise.resolve(resolved);
    else if (rejected) return Promise.reject(rejected);
  }
}

const foo = new Foo();
setTimeout(() => {
  foo.usePro().then(() => {
    console.log("end.");
  }).catch((e) => {
    console.log("error caught. " + e);
  })
}, 1000);

如果你希望能在 Foo 内部的 Promise 解决(或拒绝)之前就调用 usePro,那么当调用 usePro 时,构造并返回一个 Promise,该 Promisethis.proPromise 解决(或拒绝)后才解决。不幸的是,所需的代码要稍微复杂一些:

class Foo {
  constructor() {
    this.callProms = [];
    setTimeout(() => {
      this.pro = new Promise(() => {
        throw new Error('Problem!');
      })
      .then((resolved) => {
        this.proValue = { resolved };
      })
      .catch((rejected) => {
        this.proValue = { rejected };
      })
      .finally(() => {
        console.log('internal promise finishing');
        this.resolveCalls();
      });
    }, 1000);
  }
  resolveCalls() {
    this.callProms.forEach((resolve) => {
      resolve(this.getProValue());
    });
  }
  getProValue() {
    const { proValue } = this;
    if (!proValue) return;
    const { resolved, rejected } = proValue;
    if (resolved) return Promise.resolve(resolved);
    else if (rejected) return Promise.reject(rejected);
  }
  usePro() {
    return this.getProValue()
    || new Promise((resolve) => {
      this.callProms.push(resolve);
    });
  }
}

console.log('Starting');
const foo = new Foo();

// Immediate call of `usePro`:
foo.usePro().then(() => {
  console.log("end.");
}).catch((e) => {
  console.log("immediate error caught. " + e);
})

// Delayed call:
setTimeout(() => {
  foo.usePro().then(() => {
    console.log("end.");
  }).catch((e) => {
    console.log("delayed error caught. " + e);
  })
}, 2000);


如果在承诺解决之前调用usePro(),则此方法将无法正常工作。 - Jonas Wilms
你是对的,看到编辑了。有点丑陋,我想不出绕过手动构建每个早期调用usePro的新Promise的方法。 - CertainPerformance
1
这太复杂了。为什么不直接将proValue存储在一个本身永远不会被拒绝的Promise中呢?你基本上是在重新发明reflect - Bergi

0
你可以使用 Promise.all 来延迟解决:
  const delay = ms => new Promise(res => setTimeout(res, ms));

  Promise.all([
      foo.usePro(),
      delay(1000)
  ]).then(() => { 
     console.log("end.");
  }).catch(() => { 
     console.log("error."); 
  });

这样.catch就直接附加了,但是then回调会在延迟后执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接