为啥我们更喜欢在角度中使用 $q 而不是 $http [重复]
Posted
技术标签:
【中文标题】为啥我们更喜欢在角度中使用 $q 而不是 $http [重复]【英文标题】:Why do we prefer using $q in angular instead of $http [duplicate]为什么我们更喜欢在角度中使用 $q 而不是 $http [重复] 【发布时间】:2016-10-31 00:23:48 【问题描述】:我目前正在使用 Angular 的 $q 服务来进行这样的 API 调用:
var deferred = $q.defer();
$http.get(config.apiHost + details.url)
.success(function (data)
deferred.resolve(data);
).error(function (msg)
deferred.reject(msg);
);
return deferred.promise;
但我们也可以不使用 $q 来使用这种方法:
return $http.get(config.apiHost + details.url)
.success(function (data)
return data;
).error(function (msg)
return msg;
);
并且由于 $http 本身返回承诺,我还可以使用更简化的方法:
$http.get(config.apiHost + 'posts')
.success(function (data)
console.log(data)
).error(function (msg)
console.log(msg);
);
那么 $q 和 $http 之间的所有这些特别是有什么区别,因为它们都返回 promise 并且都是 async ? angular 是否为 $q 提供了一些额外的功能? 我找不到任何好的答案。
【问题讨论】:
您可以使用 $q 与其他异步操作一起返回 Promise。 $http 从 AJAX 调用返回一个承诺。 其他操作如? 喜欢阅读文件或者需要使用$timeout$q
主要仅用于与默认情况下不支持 Promise 的库兼容和当您不能依赖 Promise 的本机实现时。没有理由(对你)以其他方式使用它。例如,如果您想创建一个基于承诺的 $timeout
。
好的,这意味着对于 API 调用,我们可以简单地使用 $http 而不是 $q,因为它们都返回 promise。 $q 仅在某些库本身不支持 Promise 时才有用。
【参考方案1】:
在 Angular 中,大多数服务只返回 Promise,但在某些情况下,您希望使用 $q
创建自己的延迟对象。
案例 1
当您使用不支持 Promise 的库或您创建了自己的函数并想要返回 Promise 时。
案例 2
当您使用任何默认返回承诺但您希望在某些条件下根据某些条件返回单独承诺的构造时。
示例:在角度 $http
中仅返回一个承诺,但现在如果您想要,如果此承诺的响应包含特定值,那么只有您想返回已解决,否则返回失败,那么您需要创建自己的deffered object
,需要根据$http
响应返回的值来解决或失败。
【讨论】:
您不需要这样做。承诺链。你可以很容易地做到$http.get().then(...)
并且从then
返回的值将被传递给链中的下一个.then。无需为此使用您自己的延迟对象。
我说的是 $http..like 的响应,如果您希望响应包含某个值而不是仅解决承诺或拒绝。
这个答案不准确,描述的是常见的延迟反模式
如果你建议你想根据响应的值拒绝一个承诺,是的,我会同意你 - 这是你想要使用 $q
的一种情况(但您仍然可以返回 $q.reject()
而不是使用延迟的反模式)...但这似乎是一个非常狭窄的用例。
但这是正确的....否则角度服务已经返回承诺..为什么要在它们之上使用延迟对象。【参考方案2】:
$q
主要仅用于与默认情况下不支持 Promise 的库兼容,并且当您不能依赖 Promise
的本机实现时(例如 - 在 IE9 等旧浏览器中)。没有理由(对你)以其他方式使用它。例如,如果您想创建一个基于承诺的$timeout
。出于这些确切原因,$http
本身在后台使用 $q
。
与其他(已删除)答案所建议的不同,您确实不需要使用$q
来“存储”$http
承诺的结果。我不建议存储承诺(因为这往往会导致意大利面条代码),但如果你必须绝对这样做,你可以只存储来自$http
的承诺;承诺只执行一次。
在 promise 已解决/拒绝后传递给 then
的任何函数都将在下一个 tick 时解决,而无需重新调用最初创建 promise 的原始操作 - IOW,promise 的结果被记忆在那个对象内。
还要注意承诺链,这超出了这个答案的范围,但它本质上意味着以下代码是等价的
function legacyGet()
const deferred = $q.defer()
$http.get('http://www.google.com')
.then((response) => deferred.resolve(Object.assign(response, foo: bar))
.catch((error) => deferred.reject(error))
return deferred.defer
function modernGet()
return $http.get('http://www.google.com')
.then((response) => Object.assign(response, foo: bar))
总结一下:你的标题是错误的。我们不喜欢使用 $q,我们只谨慎地使用它。最好使用 ES6 Promise,除非你需要支持没有它的浏览器并且你不能使用 polyfill。
【讨论】:
【参考方案3】:$http
使用$q
,第一个例子是多余的,第二个也是。你只需要返回$http.get
返回的promise:
return $http.get(config.apiHost + details.url);
上面和你的第一段代码是一样的。
另外,return msg
与deferred.reject(msg)
不同。相当于throw msg
或return $q.reject(msg)
另外需要注意的是success
和error
是非标准的,你要使用then
和catch
。
【讨论】:
我不会说success
和 error
是“非标准的”,只是不推荐使用:docs.angularjs.org/api/ng/service/$http
@pulse0ne 如果您谈论的是 A+ 承诺规范,它们是非标准
@DanPantry 啊,是的,我误以为您指的是角度 $http 服务。关于规范,它是非标准的,几乎可以肯定为什么有角的人已经弃用它了。以上是关于为啥我们更喜欢在角度中使用 $q 而不是 $http [重复]的主要内容,如果未能解决你的问题,请参考以下文章
为啥现实世界的服务器更喜欢 gzip 而不是 deflate 编码?
为啥我们更喜欢授权标头将承载令牌发送到服务器而不是 URL 编码等其他技术
为啥游戏逻辑更喜欢 update() 而不是 didFinishUpdate?