我如何知道哪些处理程序在 promise 中抛出错误?
Posted
技术标签:
【中文标题】我如何知道哪些处理程序在 promise 中抛出错误?【英文标题】:How do I know which handlers throw error in promise? 【发布时间】:2017-01-06 12:34:23 【问题描述】:假设我有如下承诺:
p.then(Task1)
.then(Task2)
.then(Task3)
.catch(errorHandler);
当Task2遇到错误时,如何知道错误来自catch中的Task2?
【问题讨论】:
在Task2中你可以做try...code catch(err)return Promise.reject("rejected at task 2: " + err)
最简单的方法是让每个Task抛出一个有意义的错误
@Redu try/catch 仅适用于同步错误。我收集到 OP 也想从 Task2 中捕获异步错误。
@jib 异步错误非常容易。您只需进行异步调用并返回返回的 Promise 并在下一阶段的 fulfilled
或 rejected
回调中处理结果或错误。任何结果或错误都应返回到下一阶段处理。我想可能有三件事出错了。 1) 一个不受控制的同步错误(尝试 catch 并从 catch 块中拒绝) 2) API 返回承诺被拒绝.. 只需将其返回到下一个 then 阶段,3) 受控错误(如数据不足或缺少参数)返回一个 @ 987654325@
你将如何在同步代码中做到这一点?
【参考方案1】:
大家!我自己研究过演示代码。
希望大家能点评一下我的回答,好不好。
简介:
它展示了如何在每个处理程序中跟踪承诺,使用定制的错误处理程序来捕获错误。了解promise的工作流程。
您可以复制以下演示代码并粘贴到您的 node.js 中。根据示例和日志信息,开发者学习promise是有好处的。
用到的promise模块如下:
蓝鸟演示代码如下:
var Promise = require('bluebird');
// You can input any argument in this function to test workflow of promise
function testPromise(input)
let promise = Promise.resolve(input);
promise
.then(makeTask('Task1'))
.then(makeTask('Task2'))
.then(makeTask('Task3'))
.catch(makeErrorPredicate('Task1'), taskLog('Task1'))
.catch(makeErrorPredicate('Task2'), taskLog('Task2'))
.catch(makeErrorPredicate('Task3'), taskLog('Task3'))
// define task handler functions
function makeTask(task)
return function task1Handler(input)
if (input === task)
throw new Error(task)
return input
// custom error that it checks error message
function makeErrorPredicate(msg)
return function taskError(err)
var result = err.message === msg;
console.log(msg + ': ' + result)
return result;
// hint the error when the error has matched
function taskLog(msg)
return function thelog(err)
console.log('It\'s ' + msg)
例子:
>testPromise('Task1')
Task1: true
It's Task1
>testPromise('Task2')
Task1: false
Task2: true
It's Task2
>testPromise('Task3')
Task1: false
Task2: false
Task3: true
It's Task3
从上面的例子我们可以知道:
输入为'Task1'时,路由为:
firstHandler -> firstCatcher
输入为'Task2'时,路由为:
firstHandler -> secondHandler -> firstCatcher -> secondCather
输入为'Task3'时,路由为:
firstHandler->secondHandler->thirdHandler->firstCatcher->secondCatcher->thirdCatcher
所以,从上面我们知道的结果,我们可以理解promise是如何工作的。
如果每个人都对这个答案感到满意,请告诉我,谢谢。
【讨论】:
makeTaskHandler
应该命名为makeTask
或makeThrowTask
,而makeTaskError
应该命名为makeErrorPredicate
或makeMessageMatcher
之类的。
@Bergi 我同意你的建议!【参考方案2】:
由于 Promise 链无法保存此信息,因此您需要将其存储在某个地方。两种解决方案:
修饰错误:
p.then(Task1)
.then(function()
try
Task2();
catch (e)
e.isFromTask2 = true;
throw e;
)
.then(Task3)
.catch(errorHandler); // now you can check err.isFromTask2
使用承诺链的作用域:
let isFromTask2 = false;
p.then(Task1)
.then(function()
try
Task2();
catch (e)
isFromTask2 = true;
throw e;
)
.then(Task3)
.catch(errorHandler); // now you can check isFromTask2
【讨论】:
try
/catch
在 Task2
异步时不起作用。请参阅下面的 Redu 和 Jib 的答案。【参考方案3】:
大多数浏览器都支持error.stack
属性,一些现代浏览器甚至支持异步调用栈:
let a = () => Promise.resolve();
let b = () => Promise.reject(new Error("Fail"));
let c = () => Promise.resolve();
Promise.resolve().then(a).then(b).then(c).catch(e => console.log(e.stack));
在 Firefox 48 中,它输出:b@http://stacksnippets.net/js:14:30
。
在 Chrome 52 中,它输出:Error: Fail at b (http://stacksnippets.net/js:14:30)
。
这是最惯用的解决方案,因为它不会干扰代码的编写方式。不幸的是,并不是所有的浏览器都支持异步调用堆栈,而且输出因浏览器而异,因为它是用于调试的。
【讨论】:
这从字面上回答了“我怎么知道错误来自哪里”的问题,但我怀疑 OP 想知道“......以及如何根据来源处理它”,为此堆栈跟踪不是那么有用。 error.stack 并不是所有浏览器都真正支持 - 这可能会在缩小代码中失败。 @BenjaminGruenbaum 从链接中的兼容性表来看,至少大多数桌面浏览器似乎都支持。【参考方案4】:为了在所有浏览器中知道最终捕获中的(异步)错误来自Task2
,您可以捕获它、标记它并重新抛出它:
var tag = (e, name) => (e.isFrom = name, Promise.reject(e));
p.then(Task1)
.then(Task2)
.catch(e => tag(e, "Task2"))
.then(Task3)
.catch(errorHandler);
不幸的是,这也会捕获来自Task1
和p
的错误,因此您还需要一个“catch-bypass”:
p.then(Task1)
.then(result => Task2(result).catch(e => tag(e, "Task2")))
.then(Task3)
.catch(errorHandler);
这会使来自Task1
或更早版本的任何错误“绕过”(即绕过)我们对Task2
的捕获。
之所以有效,是因为我们将 catch 放在了 .then
的成功回调中。每个.then
都有一个隐含的第二个参数,它是一个错误处理程序,很像.catch
,除了它没有捕获相应的成功回调,在这种情况下Task2
和我们的捕获,只有链上的先前错误(即来自Task1
或任何先前步骤的错误)。
省略一个错误回调意味着“通过它不变”。即与此处的e => Promise.reject(e)
相同。
let tag = (e, name) => (e.isFrom = name, Promise.reject(e));
let a = () => Promise.resolve();
let b = () => Promise.reject(new Error("Fail"));
let c = () => Promise.resolve();
let x = () => null.f();
let foo = failFirstStep => Promise.resolve()
.then(failFirstStep? x : a)
.then(() => b().catch(e => tag(e, "b")))
.then(c)
.catch(e => console.log((e.isFrom || "something else") + " failed"));
foo(false).then(() => foo(true));
概括地说,您可以标记函数,但我发现很难以一种惯用的方式来做,而且不会影响可读性或时间,所以我将把它作为练习。在实践中,有一个 better answer,但我发现这项技术很有趣,可以涵盖,并且在某些情况下很有用。
【讨论】:
第一个也会从p
捕获错误,而不仅仅是Task1
。
传递0
和什么都不传递(或任何非函数参数)一样好。您应该忽略它,这是标准行为,0
会让每个阅读此代码的人感到困惑。
Protip:const tag = name => e => Promise.reject(e.tag = name, e);
会让你简单地使用.catch(tag("TaskN"))
@Bergi 没错,虽然我尽量避免使用.then(foo())
模式,因为它看起来很像一个常见的错误,但那可能只是我自己。
不,不只是你 :-) 命名可以简化它,tagging
或 withTag
或 makeTagger
(或您设计的任何其他方案)可能暗示该调用确实返回另一个功能。【参考方案5】:
你可以这样做;
Promise.resolve(21).then(v => v*2)
.then(v =>
try throw new Error(v + " is immutable") // assuming that your code results an error
catch(err) return Promise.reject(message: "2nd then stage threw an error", err:err)
)
.then(v => v)
.catch(err => console.log(err.message, "\n",err.err.message));
【讨论】:
Task2
被提到(我猜)作为例子,如果他们想确定错误发生在三个任务中的哪一个呢?
@robertklep 如果您知道在哪个then
阶段遇到了无法控制的错误对您很重要,那么在每个fulfillment
回调中,您应该在try
块中运行您的代码并捕获就像我展示的那样,后续的 catch
块可能出现的任何错误。一旦你发现了错误,你就可以安全地返回一个包含所有必要数据和信息的拒绝承诺。
如果任务正在执行异步操作(这是公平的假设),则使用 try/catch
将不起作用。如果任务本身也使用 Promise,function Task2() return SomeTask().catch(...)
可能是更好的解决方案。
完全正确。这里的想法是不要放过错误。但是根据您的假设,如果我们在 then
阶段处理承诺的异步 API,那么正确的做法是返回收到的承诺并在下一个阶段的 fulfillment
或 @987654331 处理结果或错误@回调。这样我们总能知道错误发生在哪个阶段,也可以避免嵌套then
s。所以最好在每个then
阶段都有rejected
回调(第二次回调)。
@Redu "在下一个阶段被拒绝的回调中处理错误。" 并没有真正起作用。被拒绝的回调(无论是then
还是catch
)处理链中any 中的all 错误,而不仅仅是前一阶段的错误。跨度>
以上是关于我如何知道哪些处理程序在 promise 中抛出错误?的主要内容,如果未能解决你的问题,请参考以下文章
Meteor 在 promise_server.js 中抛出错误?
在Service Worker中抛出错误“Uncaught(in promise)DOMException”