如何为 1 个 Promise 设置多个成功回调?
Posted
技术标签:
【中文标题】如何为 1 个 Promise 设置多个成功回调?【英文标题】:How to have multiple success callbacks for 1 promise? 【发布时间】:2016-01-26 18:46:27 【问题描述】:我有一个函数loadItems()
加载异步内容,然后失败或成功。失败和成功回调都必须在库中做一些事情,然后将其传递给实现者,实现者可以选择实现失败或成功,或两者兼而有之,或不实现。
我现在遇到的问题是,如果库同时实现了成功和失败处理程序,它将始终返回一个解析为成功的新承诺。
这就是我所拥有的:
// The library:
function loadItems()
return store.get('todo').then(function(rsp)
// Save these locally
items = rsp.value || [];
, function(rsp)
// Do nothing, but let the user know
alert(rsp.error);
);
store.get('todo')
(来自另一个库)返回一个承诺。 loadItems()
无法控制它。
// The library implementer:
function init()
loadItems().then(showItems);
我想要并期望发生的事情:
loadItems()
运行库中的异步代码 (store.get()
)
它实现了success
和fail
,因为它们具有强制操作
它将成功/失败传递给实施者
实现者只实现success
,因为它不关心错误,因为库处理了它
所以它使用.then(onSuccess)
'添加另一个成功回调'
会发生什么(有错误):
-
库的失败回调被执行
一个新的承诺被传递给实施者
新的承诺总是会成功解决
实现者的成功回调被触发,结果被破坏,因为库的成功回调没有触发
Promises 是否真的太酷了以至于不能拥有多个 sync 成功处理程序??
我可以想象甚至源库(定义store.get()
)也想要处理错误(用于偷偷记录),然后传递成功/失败。
我的“解决方案”:
-
让
loadItems()
'失败回调抛出错误。不幸的是,这意味着init()
必须抓住它(或者浏览器抱怨它)。不酷。
使用简单的回调与init()
对话,而不是仅在成功后触发的promise。这不好,因为init()
可能选择在某一天处理错误。
我确定我做错了什么,但我没有看到。
【问题讨论】:
请阅读:api.jquery.com/jquery.when 你使用的是什么承诺实现? "不幸的是,这意味着 init() 必须抓住它(或者浏览器抱怨它)。不酷。“ - 不知道你的意思。在这方面,promise 拒绝就像一个例外,它似乎正是你想要的? @CarlosCarucce 我没有使用 jQuery。 @Bergi 我在 Chrome 中使用原生 Promises。我不希望每个“实施者”都处理该错误,因为该错误已由loadItems()
处理。 init()
只关心结果是否有效。如果没有,没关系,但不要执行then()
,就好像它确实有效一样。
【参考方案1】:
如果您希望拒绝处理程序根据错误执行某些操作,但希望返回的承诺仍然被拒绝,您只需在拒绝处理代码之后重新抛出错误(或返回被拒绝的承诺)。这将允许拒绝通过返回的 Promise 传播回来。
// The library:
function loadItems()
return store.get('todo').then(function(rsp)
// Save these locally
items = rsp.value || [];
, function(rsp)
// Do nothing, but let the user know
alert(rsp.error);
// rethrow the error so the returned promise will still be rejected
throw(rsp);
);
提供一个不会抛出或返回被拒绝的 Promise 的拒绝处理程序会告诉 Promise 系统您已经“处理”了错误并且您的拒绝处理程序的返回值成为该承诺的新履行值。因此,如果您希望承诺“保持”被拒绝,但希望处理程序根据拒绝执行某些操作(记录拒绝非常常见),那么您必须在拒绝处理程序中重新抛出错误或返回拒绝承诺。
虽然这最初看起来可能违反直觉,但它为您提供了最大的灵活性,因为您可以完全处理错误并让返回的承诺“成功”解决,或者您可以选择告诉承诺系统您要传播一个错误,您甚至可以选择您想要的错误(不一定是相同的错误)。
您关于多个成功处理程序的部分问题让我有点困惑。您可以轻松地拥有多个带有任何承诺的成功处理程序:
var p = someFuncThatReturnsSuccessfulPromise();
p.then(someSuccessHandler);
p.then(someOtherSuccessHandler);
如果p
是一个成功解决的promise,那么这两个成功处理程序都将按照它们附加的顺序被调用,someSuccessHandler
中发生的事情不会影响someOtherSuccessHandler
是否被调用。如果原始承诺成功解决,那么两个处理程序将始终被调用。
如果你链接你的成功处理程序,那么它是一个完全不同的用例。这是完全不同的:
var p = someFuncThatReturnsSuccessfulPromise();
p.then(someSuccessHandler).then(someOtherSuccessHandler);
因为第二个.then()
处理程序没有附加到p
,而是附加到p.then(someSuccessHandler)
,这是一个不同的承诺,其结果可能会受到someSuccessHandler
中发生的事情的影响。
您问题中的代码是链接的。你要回来了:
return store.get().then(...)
所以,当调用者链接到那个时,完整的链是:
return store.get().then(yourhandler).then(theirhandler)
这样,yourhandler
可以影响传递给theirhandler
的结果。
除了我的第一个建议是重新抛出错误之外,您还可以这样做:
// The library:
function loadItems()
var p = store.get('todo');
p.then(function(rsp)
// Save these locally
items = rsp.value || [];
, function(rsp)
// Do nothing, but let the user know
alert(rsp.error);
);
return p;
在这里,您需要确保您的处理程序不会影响返回的内容。如果您的处理程序中没有任何异步操作并且您没有尝试更改原始 store.get()
承诺的已解决值或拒绝错误,则可以这样做。我通常不建议这样做,因为如果您的处理程序正在执行其他异步操作或想要影响返回值,这会导致问题,但它也可以在适当的情况下使用。
【讨论】:
@Deux - 这回答了你的问题吗? 我尝试了最后一件事(返回原始 Promise),但我仍然收到来自 Chrome 的“承诺中未捕获的错误”警告。我认为这是我的意思,但它并不像宣传的那样有效。如果我返回原始 Promise,并在 catch 中返回Promise.reject()
,Chrome 甚至会抛出其中 2 个警告。
@Deux - 调用loadItems()
的代码是什么样的?如果它没有拒绝处理程序,那么这是一个有效的警告,因为您有一个未处理的拒绝路径,这就是警告试图告诉您的内容。我还更改了最后一个代码块,因为没有理由在那里重新抛出异常,因为它不会改变结果(这可能会删除警告)。
它确实有一个拒绝处理程序。这就是重点:只需要 1 个拒绝处理程序。但是 Chrome 确实希望每个处理程序层都有一个拒绝处理程序。我只是加了一个空的,像 Promise 想要的 =(以上是关于如何为 1 个 Promise 设置多个成功回调?的主要内容,如果未能解决你的问题,请参考以下文章