7

Promise 的回调$http有多个参数:body、status、headers、config。

我想手动创建类似的承诺,但不知道该怎么做。我想做的或多或少是:

myservice.action().then(function(status, message, config) {
    // ...
});

我知道我可以将带有键的对象传递给回调,但希望具有与$http. 我查看了角度来源,但要么不完全理解它,要么就是不能正确地做到这一点。

你知道如何创建能够将多个参数传递给回调/错误返回的 Promise 吗?

4

1 回答 1

10

正如评论中所建议的,看看 AngularJS 的$q 实现。这些文档因……有时很难理解而臭名昭著。

但无论如何,让我们尝试一个简短的例子。我在 CoffeeScript 中执行此操作,因为我更喜欢它,但如果您愿意,您应该能够在coffeescript.org上编译示例。

让我们实现一个服务:

app = angular.module 'my.custom.services', ['your.other.modules']

app.factory 'Service', ['$http' , '$q', (http, q) ->

  # this is your deferred result
  deferred = q.defer()

  get: ->
    deferred.promise
]

这很容易。它只是一个服务,它将使用$qand $http,因为 a) 我们想要一些我们正在谈论的基于承诺的甜蜜东西 b) '$http' 本身就是一个很好的例子,它可以调用异步和其结果无法立即获得。

这里有趣的部分是get这里返回的对象部分。请注意,该服务是作为 实现的factory,而不是作为service. 有关差异,请参见此处。我通常认为它是服务的“花哨”版本,在公开服务的 API 之前,我只有一些额外的空间用于我自己的逻辑(这确实意味着不同的东西,但那是另一个故事。

无论如何,调用时get将返回promise延迟对象的。是promise一个对象,它暴露了一个then方法。使用此服务时,您可能会像这样注入它:

app = angular.module 'my.custom.application', ['my.custom.services']

app.controller 'AppController', ['Service', (service)->

  service.get() # then what?

]

正如我所提到的,get只会返回一个承诺。承诺,就像在现实生活中一样,必须在某个地方得到解决。所以让我们在服务中这样做——只要我们完成了我们承诺的任务,我们的承诺就会得到解决。这可能类似于通过 AJAX 调用 URL 或大计算(有人知道第 117 个斐波那契数是什么吗?)。

对于我们的示例,我们使用 http-call,因为我们现在不使用,无论它是否以及何时返回给我们:

app.factory 'Service', ['$http' , '$q', (http, q) ->

  # this is your deferred result
  deferred = q.defer()

  # this is where http is used, this is started immediately, but takes a while
  http.get('some-resource.json').then (response) ->
    # now 'response' is the whole successful response, it has a data object with the payload
    if !someCondition
      deferred.resolve response.data #we have what we wanted
    else
      deferred.reject {error: "Failed", additional: "foo", number: 2} #we ran into some error

  get: ->
    deferred.promise
]

基于someCondition此,如果我们愿意,我们可以故意让请求失败。如果您想自己尝试,也可以timeout在文档中使用like。

现在会发生什么?好吧,我们仍然有那个控制器:

app.controller 'AppController', ['Service', (service)->

  service.get().then(successCallback, errCallback)

]

正如我所解释的,promise公开了一个then带有签名的方法then(success, error),其中successerror是将我们解析的任何内容作为参数的函数,例如:

app.controller 'AppController', ['Service', (service)->
  successCallback = (data) ->
    # we can work with the data from the earlier resolve here
    scope.data = data

  errCallback = (err) ->
    # the error object, we got from the rejection
    console.log err.error # => "Failed"

  service.get().then(successCallback, errCallback)

]

如果您希望将多个值传递给回调,我建议您在解析/拒绝承诺时传递一个对象。您还可以为 promise 进行命名回调,就像 angular 在它的$http实现中所做的那样:

app.factory 'Service', ['$http' , '$q', (http, q) ->

  # this is your deferred result
  deferred = q.defer()

  # [...]

  get: ->
    promise = deferred.promise

    promise.success = (fn) ->
      promise.then (data) ->
       fn(data.payload, data.status, {additional: 42})
      return promise

    promise.error = (fn) ->
      promise.then null, (err) ->
        fn(err)
      return promise

    return promise 
]

本质上,您扩展了由方法返回的承诺success,该方法将单个方法作为回调,等待承诺被解决,然后使用回调。如果您愿意,您可以对任何其他方法执行相同的操作(有关提示,请参阅角度实现

在您的控制器中,您可以像这样使用它:

service.get().success (arg1, arg2, arg3) ->
  # => arg1 is data.payload, arg2 is data.status, arg3 is the additional object
service.get().error (err) ->
  # => err

这是基础,你可以尝试,如果你想,我建议你尝试以下:

  • 具有多个延迟承诺的服务中的多个承诺
  • 尝试另一种延迟结果的方法,例如长时间计算

而且,作为奖励:

  • 尝试将成功/错误回调隔离到服务的命名方法中

这个例子使用了$qAngular 中的实现,当然你也可以使用其他的 Promise 实现,比如这个,它是$q.

于 2013-07-16T21:27:43.900 回答