拒绝AngularJS中带有多个参数(如$http)的承诺

Rejecting promises with multiple arguments (like $http) in AngularJS

本文关键字:http 承诺 参数 AngularJS 拒绝      更新时间:2023-09-26

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

我想亲手创造类似的承诺,但不知道如何做到这一点。我想做的或多或少是:

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

我知道我可以将带有键的对象传递给回调,但希望有类似于$http的约定。我观察了角度源,但要么没有完全理解,要么就是做不好。

你知道如何创建能够将多个参数传递给回调/errbacks的promise吗?

正如评论中所建议的,看看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
]

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

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

无论如何,get在被调用时将返回延迟对象的promisepromise是一个对象,它公开了一个then方法。当使用此服务时,您可能会将其注入如下:

app = angular.module 'my.custom.application', ['my.custom.services']
app.controller 'AppController', ['Service', (service)->
  service.get() # then what?
]

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

对于我们的例子,我们使用http调用,就像我们现在不使用的那样,无论它是否以及何时会返回给我们:

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

现在发生了什么?好吧,我们还有那个控制器:

app.controller 'AppController', ['Service', (service)->
  service.get().then(successCallback, errCallback)
]

正如我所解释的,promise公开了一个具有签名then(success, error)then方法,其中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时传递一个对象。您还可以为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返回的promise,该方法将单个方法作为回调,等待promise得到解析,然后使用回调。如果你想的话,你可以对任何其他方法做同样的事情(有关提示,请参阅角度实现)

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

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

这是最基本的,如果你愿意,你可以进行实验,我建议你尝试以下方法:

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

此外,作为奖励:

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

这个例子使用了Angular中的$q实现,当然可以使用另一个实现来实现promise,比如这个,它是$q的基础。