0

I've found myself inventing a pattern using jQuery promises and wondered to myself if it already exists somewhere else.

Basically, I have a chain of asynchronous actions that need to occur one after the other - but they're not necessarily all the same action, the actions may be conditional.

It might look like this (CoffeeScript syntax for brevity):

a = itemA.save()
a.done ->
  b = itemB.fetch()
  b.done ->
    if x
      c = itemC.fetch()
      c.done ->
        ui.showDone()
    else
      ui.showDone()

Note the ugliness:

  • Excessive nesting
  • Repeated code

My composition routine reduces the nesting. In use, it looks like this:

chain = itemA.save()
chain = andThen chain, ->
  itemB.fetch()
chain = andThen chain, ->
  if x
    itemC.fetch()
chain.always ->
  ui.showDone()

Where andThen looks like this:

andThen = (promise, doneCallback) ->
  result = $.Deferred()
  promise.fail (value) ->
    result.reject(value)
  promise.done (value) ->
    cbResult = doneCallback(value)
    if cbResult && cbResult['done']
      cbResult.done (value) ->
        result.resolve(value)
      cbResult.fail (value) ->
        result.reject(value)
    else
      result.resolve(cbResult)
  result.promise()

I'm probably reinventing the wheel. But I haven't found this wheel anywhere else. Where is it?

I'm primarily using promises in order to abstract out things like showing error messages, disabling buttons while requests are in flight, etc. Rather than embedding these things inline, using promises I'm able to hand off the promise to a component that is able to react to the success or failure of an ongoing action. It makes UI more consistent and composable.

This means I need a promise that represents the whole chain of multiple deferreds - and that will be resolved no matter what path is taken.

Barry Kelly
  • 41,404
  • 5
  • 117
  • 189

1 Answers1

1

I'm probably reinventing the wheel. But I haven't found this wheel anywhere else. Where is it?

It even has a similar name: the .then() method! It's the main idea of promises actually.

itemA.save().then ->
  itemB.fetch()
.then ->
  if x
    itemC.fetch()
.always ->
  ui.showDone()
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • I read and re-read the documentation on then, and convinced myself that it was almost indistinguishable from done except for some filtering logic that had been added after pipe was deprecated. But now going back to it, I see that an "observable" object returned by the callback will be composed properly. – Barry Kelly Jun 12 '14 at 18:22