在AngularJS中拒绝具有多个参数(如$ http)的promise

gra*_*hez 7 javascript angularjs

$httppromises的回调有多个参数:body,status,headers,config.

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

myservice.action().then(function(status, message, config) {
    // ...
});
Run Code Online (Sandbox Code Playgroud)

我知道我可以通过键传递对象来回调,但是希望有类似的约定$http.我看一下角度来源,但要么完全不理解,要么就是不能做到这一点.

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

Flo*_*ian 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
]
Run Code Online (Sandbox Code Playgroud)

这个很容易.它只是一个服务,它将利用$q$http,因为a)我们想要一些我们正在讨论的基于甜蜜承诺的东西和b)'$ http'本身就是一个很好的例子,它可以调用异步和哪个结果不能马上获得.

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

无论如何,在调用时get将返回promise延迟对象.这promise是一个公开then方法的对象.使用此服务时,您可能会将其注入:

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

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

  service.get() # then what?

]
Run Code Online (Sandbox Code Playgroud)

正如我所提到的,get只会回复一个承诺.像现实一样,承诺必须在某个地方得到解决.所以,让我们在服务中做到这一点 - 只要我们完成了我们承诺的任务,我们的承诺就会得到解决.这可能类似于通过AJAX调用URL或大计算(任何人都知道第117个Fibonacci数是什么?).

对于我们的示例,我们使用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
]
Run Code Online (Sandbox Code Playgroud)

根据someCondition我们可以根据需要让请求失败.如果你想尝试自己,你也可以timeout在文档中使用类似的东西.

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

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

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

]
Run Code Online (Sandbox Code Playgroud)

正如我所解释的那样,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)

]
Run Code Online (Sandbox Code Playgroud)

如果你想将多个值传递给回调,我建议你在解析/拒绝承诺时传递一个对象.你也可以为promise做一个命名回调,就像它的$http实现中的angular一样:

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 
]
Run Code Online (Sandbox Code Playgroud)

您实质上扩展了一个方法所提供的承诺,该方法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
Run Code Online (Sandbox Code Playgroud)

这是基础知识,您可以根据需要进行试验,我建议您尝试以下方法:

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

而且,作为奖励:

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

这个例子使用$qAngular中的实现,你当然可以使用另一个promises实现,就像这个,它是基础$q.