5

I have the following code:

authService.authenticate()
.then(function (user) {
  return Task.all({user: user})
})
.then(function (tasks) {
  // How to access user object here?
})

Is there some built-in way to pass user object to the second then function without doing something like this:

var user2 = null;
authService.authenticate()
.then(function (user) {
  user2 = user
  return Task.all({user: user})
})
.then(function (tasks) {
   // Use user2 here
})

or this:

authService.authenticate()
.then(function (user) {
  var defer = $q.defer()
  Task.all({user: user}).then(function (tasks) {
    return defer.resolve(user, tasks)
  })
  return defer.promise
})
.then(function (user, tasks) {
   // Use user2 here
})

or nesting them by calling the second then directly on Task.all (this way I'd have user object available via closure)? Nesting them is exactly what I'm trying to avoid.

Roman C
  • 49,761
  • 33
  • 66
  • 176
szimek
  • 6,404
  • 5
  • 32
  • 36

2 Answers2

2

You can put the then within the scope where user is still accessible (check closures)

authService.authenticate()
.then(function (user) {
  Task.all({user: user})
  .then(function (tasks) {
    // How to access user object here?
  })
})

From Kriskowal's Q documentation itself, both styles are equivalent. Angular's $q is based on Q

The only difference is nesting. It’s useful to nest handlers if you need to capture multiple input values in your closure.

kumarharsh
  • 18,961
  • 8
  • 72
  • 100
  • Thanks, but that's the third case I was talking about in my question - nesting them :) I'm trying to avoid it, because I just showed a simplified case - in the real code there a few more promises, so nesting them would be just like using callbacks. – szimek Nov 23 '13 at 18:36
  • hmm... yes, you're right. The thing is that I'm using this nested style in my code, and the code is really really big... The level of nesting isn't really so large. Also, Coffeescript syntax helps a lot ;) – kumarharsh Nov 23 '13 at 18:54
  • I'm using CoffeeScript as well, so I'll try nesting them and see how it looks like. Though I'm not exactly sure how error handling works in case of nested promises... – szimek Nov 23 '13 at 19:30
  • errors bubble up until it finds a fail so you'll need a fail at the end. – kumarharsh Nov 23 '13 at 19:35
  • 1
    @szimek it works exactly the same way see https://github.com/kriskowal/q#chaining – Esailija Nov 24 '13 at 01:20
1

Could combine user and tasks into one object to pass to next then

authService.authenticate()
.then(function (user) {
  var defer = $q.defer()
  Task.all({user: user}).then(function (tasks) {

    return defer.resolve({user:user, tasks:tasks})
  })
  return defer.promise
})
.then(function (data) {
   $scope.user=data.user;
   $scope.tasks=data.tasks;
})

DEMO

charlietfl
  • 170,828
  • 13
  • 121
  • 150
  • Thanks! That's what I'm doing in the second example, although only after posting it, I found out that I can pass only a single argument to `defer.resolve`, so my example with `defer.resolve(user, tasks)` is slightly incorrect. It seems to be the cleanest solution for me, although requires creating a new defer object manually, which may suck a lot if you need e.g. to pass a variable through the whole chain. – szimek Nov 23 '13 at 23:03
  • could store various steps in a service object, just return that object at the end – charlietfl Nov 23 '13 at 23:10
  • I'm unfamiliar with Q/Angular promises, but if `Task` happens asynchronously, does this guarantee that `defer` will contain both the `user` and `tasks` data by the time the second `then` body occurs? If I understand correctly, `defer.resolve()` is mutating the state of the `defer` object, so how does returning `defer.promise` know whether or not the `Task.all` then body terminated? I'm trying to understand how this solution works. – osdiab Nov 13 '14 at 06:48
  • -1 for using `defer` here, because now you've silently dropped the errors from `Task.all` - just use `return Task.all(...).then(...)` – Eric Oct 11 '17 at 21:25