如何为 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()) 它实现了successfail,因为它们具有强制操作 它将成功/失败传递给实施者 实现者只实现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 设置多个成功回调?的主要内容,如果未能解决你的问题,请参考以下文章

如何为多个动态 div 设置多个 Cookie

ES6:何为Promise?

Promise

Xcode iOS - 使用相同资源的多个测试失败 - 如何为多个测试使用 1 个资源?

关于promise的几个认知

JavaScript Promise.all 是不是有在成功和失败时触发的回调 [重复]