为啥我们更喜欢在角度中使用 $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 msgdeferred.reject(msg) 不同。相当于throw msgreturn $q.reject(msg)

另外需要注意的是successerror是非标准的,你要使用thencatch

【讨论】:

我不会说 successerror 是“非标准的”,只是不推荐使用:docs.angularjs.org/api/ng/service/$http @pulse0ne 如果您谈论的是 A+ 承诺规范,它们非标准 @DanPantry 啊,是的,我误以为您指的是角度 $http 服务。关于规范,它是非标准的,几乎可以肯定为什么有角的人已经弃用它了。

以上是关于为啥我们更喜欢在角度中使用 $q 而不是 $http [重复]的主要内容,如果未能解决你的问题,请参考以下文章

为啥现实世界的服务器更喜欢 gzip 而不是 deflate 编码?

为啥我们更喜欢授权标头将承载令牌发送到服务器而不是 URL 编码等其他技术

为啥 JSLint 更喜欢点符号而不是方括号?

为啥游戏逻辑更喜欢 update() 而不是 didFinishUpdate?

为啥约束更喜欢调整一个堆栈的大小而不是另一个,你如何改变这种偏好? (xCode)

为啥 FetchContent 更喜欢子目录包含而不是安装依赖项?