如何在Angular.js中实现当Promise被执行时始终运行某些代码

83
在我的Angular.js应用程序中,我正在运行一些异步操作。在它开始之前,我使用一个模态div覆盖应用程序,然后一旦操作完成,无论成功与否,我都需要移除该div。
目前我的代码是这样的:
LoadingOverlay.start(); 
Auth.initialize().then(function() {
    LoadingOverlay.stop();
}, function() {
    LoadingOverlay.stop(); // Code needs to be duplicated here
})

它工作得很好,但我更喜欢像这个伪代码一样更简洁的东西:

LoadingOverlay.start(); 
Auth.initialize().finally(function() { // *pseudo-code* - some function that is always executed on both failure and success.
    LoadingOverlay.stop();
})

我认为这是一个相当普遍的问题,所以我想它可能可以解决,但在文档中找不到任何相关内容。有没有想法它是否可以完成?


2
@Beetroot-Beetroot,这样做是不行的,因为如果initialize()失败,你仍然需要声明一个“成功”函数和一个“失败”函数,并在其中复制代码。 - laurent
1
Laurent,你想要的在Angular的轻量级$q服务中目前不可用,该服务提供了只有一个方法.then()的Promise - 可以在这里查看"The Promise API" here。唯一的自由是拥有一个.then()或链接多个.then()。你不是第一个希望有更广泛的Promise API的人 - 你想要的功能已经在这里正式请求here - Beetroot-Beetroot
1
显然,在Angular 1.2.6中没有实现或回滚always(callback)。我们现在必须使用finally。我想知道为什么保留字finallyalways更好。 - Aleyna
@Aleyna,有趣的是,当我实施该功能时,共识是使用always,因为这是jQuery使用的方式,并因为finally是一个保留的JS关键字。最终,他们似乎还是选择了finally,但是有一个警告:由于“finally”是JavaScript中的保留字,并且ES3不支持保留关键字作为属性名称,因此您需要像“promise['finally'](callback)”一样调用该方法以使您的代码兼容IE8。为什么不呢,但这似乎比必要的麻烦些。 - laurent
实际上,如果不使用引号,Android浏览器也会抛出错误,如果使用finally - laurent
显示剩余9条评论
4个回答

164

这个特性已经在这个拉取请求中实现,并且现在成为AngularJS的一部分。最初它被称为"always",后来重命名为finally,因此代码应该如下:

LoadingOverlay.start(); 
Auth.initialize().then(function() {
    // Success handler
}, function() {
    // Error handler
}).finally(function() {
    // Always execute this on both error and success
});

请注意,由于 finally 是一个保留关键字,因此可能需要将其转换为字符串,以便在某些浏览器(如 IE 和 Android 浏览器)上不会出现错误:

$http.get('/foo')['finally'](doSomething);

10
对于从网络搜索中找到这篇文章的任何人,答案中提到的链接是指函数 always,但它已被更改为 finally,您可以在此提交(或源代码)中看到:https://github.com/angular/angular.js/commit/f078762d48d0d5d9796dcdf2cb0241198677582c - Austin Thompson
@AustinThompson,谢谢你提供的信息,我已经更新了帖子。 - laurent
@this.lau_ finally()必须是链式调用中的最后一个调用吗?还是我可以在它之后继续链式调用then()呢? - Brian
2
您,先生,让我开心了一整天! - JacobF
4
@Brian finally返回的是一个与其他方法类似的promise,因此可以进行链式调用。然而(至少在某些版本的Angular中),方便的successerror重载仅添加到$http的直接返回值中,因此如果你从finally开始,就会失去这些方法。 - drzaus
@drzaus,记录一下,successerror已被弃用。https://docs.angularjs.org/api/ng/service/$http - greenoldman

7

我正在使用Umbraco 7.3.5版本的后端和AngularJS 1.1.5版本,并发现了这个线程。当我实施批准的答案时,我遇到了错误:

xxx(...).then(...).finally不是一个函数

然而确实有效的是always。如果其他使用旧版本的AngularJS的人发现本线程并且无法使用finally,请改用此代码。

LoadingOverlay.start(); 
Auth.initialize().then(function() {
    // Success handler
}, function() {
    // Error handler
}).always(function() {
    // Always execute this on both error and success
});

2

对于那些没有使用angularJS的人,如果您可以接受捕获错误(不确定.finally()是否能做到这一点),您可以使用.catch().then()来避免重复的代码。

Promise.resolve()
  .catch(() => {})
  .then(() => console.log('finally'));

即使是为了日志记录或其他清理工作,catch()也可能会变得有用。

https://jsfiddle.net/pointzerotwo/k4rb41a7/

1

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