30

Is there a way to chain Promises together in Coffeescript. For example, consider the following javascript code,

return $.getJSON('/api/post.json')
  .then(function(response) {
    // do something
  })
  .then(function(response) {
    // do something
  })
  .then(null, function(err) {
    // do something
  });

Each of the then's is optional, and the final then needs to be returned by the function. Currently I am writing this in coffeescript as,

promise = $.getJSON('/api/post.json')
promise = promise.then (response) ->
  // do something

promise = promise.then (response) ->
  // do something

promise = promise.then null, (err) ->
  // do something

return promise

Is there a better way to do this? Thanks.

Darshan Sawardekar
  • 5,065
  • 2
  • 21
  • 31
  • You could also take a look at IcedCoffeeScript. It works a little differently, but it works quite well. –  Jul 07 '13 at 17:18

3 Answers3

44

Ezekiel shows the right way, but it doesn't need the parentheses around the functions. Just do:

$.getJSON '/api/post.json' # As of CoffeeScript 1.7, you don't need the parentheses here either.
.then (response) ->
  # do something
  response # if you would not return anything, promise would be fulfilled with undefined
.then (response) ->
  # do something
  undefined # necessary to prevent empty function body
.then null, (err) ->
  # handle error

I think it's surprisingly clean. The one thing that's relatively messy is when you need to add onRejected and onFulfilled handlers at the same time.

Note: Last time I checked, this did not work in CoffeeScript Redux, but this was a few months ago.

Note 2: You need at least one line of actual code (i.e. not just a comment) in each function body for this to work. Typically, you will, so it's not a big issue.

Myrne Stol
  • 11,222
  • 4
  • 40
  • 48
  • Downvoted because this does not compile in CoffeeScript 1.4.0 or 1.6.3 (latest). The parentheses as shown in my answer are required. – Ezekiel Victor Jul 08 '13 at 07:41
  • 4
    @EzekielVictor: Thanks for being vigilant. I just looked into it, and problem seems to be that the second part of the chain lacked a function body. The indented comment was seemingly not enough. I added an explicit "undefined" at the end so it compiles. I've never run into this issue because I've never had non-empty function bodies in these places. – Myrne Stol Jul 08 '13 at 08:36
  • Ah, thank you for replying. I removed DV... And I learned something useful there. ;) – Ezekiel Victor Jul 09 '13 at 08:13
  • $.getJSON('/api/post.json'.then(function(response) { return response; }).then(function(response) { return void 0; }).then(null, function(err) {})); – Ramon de Klein Jul 26 '14 at 01:02
  • Note 2 is important. Spent the last 20min trying to figure out what was wrong. Comments alone in promises will not work or compile successfully with coffeescript. – d_ethier Sep 28 '14 at 02:47
14

This is my personal favorite way to write promises, with a little bit extra indentation

doSomething = () -> new RSVP.Promise (resolve, reject) ->
  if 1 is 1
    resolve 'Success'
  else
    reject 'Error'

doSomething()
.then (res) ->
      console.log 'Step 1 Success Handler'

    , (err) ->
      console.log 'Step 1 Error Handler'

.then (res) ->
      console.log 'Step 2 Success Handler'

.then (res) ->
      console.log 'Step 3 Success Handler'

    , (err) ->
      console.log 'Step 3 Error Handler'

Which compiles to:

var doSomething;

doSomething = function() {
  return new RSVP.Promise(function(resolve, reject) {
    if (1 === 1) {
      return resolve('Success');
    } else {
      return reject('Error');
    }
  });
};

doSomething().then(function(res) {
  return console.log('Step 1 Success Handler');
}, function(err) {
  return console.log('Step 1 Error Handler');
}).then(function(res) {
  return console.log('Step 2 Success Handler');
}).then(function(res) {
  return console.log('Step 3 Success Handler');
}, function(err) {
  return console.log('Step 3 Error Handler');
});

There are some instances where this works really well too:

step1Success = (res) -> console.log 'Step 1 Success Handler'
step1Error   = (err) -> console.log 'Step 1 Error Handler'

step2Success = (res) -> console.log 'Step 2 Success Handler'

step3Success = (res) -> console.log 'Step 3 Success Handler'
step3Error   = (err) -> console.log 'Step 3 Error Handler'

doSomething()
  .then(step1Success, step1Error)
  .then(step2Success)
  .then(step3Success, step3Error)

Tested on coffee-script v1.6.3

James Kyle
  • 4,138
  • 4
  • 28
  • 41
  • This works nicely, and the only thing that hung me up for a bit is ensuring you don't have any spaces in the code. This is probably common for those new to coffeescript, but I couldn't get code like that above to compile until I realized I had a single space in the set of tabs. – Sam Storie Oct 18 '14 at 17:39
4

This is probably the best you'll do:

$.getJSON('/api/post.json')
    .then( (response) ->
      # do something
    ).then( (response) ->
      # do something
    ).then null, (err) ->
      # do something

Note the parentheses surrounding the then() arguments. Nothing earth shattering but hopefully this helps.

Ezekiel Victor
  • 3,877
  • 1
  • 27
  • 28