Async code without callbacks with CoffeeScript generators

Sometimes it’s very hard to understand code with a bunch of callbacks, even if it with promises. But in ES6 and in CoffeeScript 1.9 we got generators, so maybe we can avoid callbacks with them, and use something like tornado.gen?

And we can, let’s look at this little helper function:

gen = (fn) ->
  new Promise (resolve, reject) ->
    generator = fn()

    putInGenerator = (method) -> (val) ->
        handlePromise generator[method](val)
      catch error
        reject error

    handlePromise = ({value, done}) ->
      if done
        resolve value
      else if value and value.then
        value.then putInGenerator('next'), putInGenerator('throw')
        reject "Value isn't a promise!"


With it code like:

$http.get('/users/').then ({data}) ->
  doSomethingWithUsers data.users
  $http.get '/posts/'
, (err) ->
  console.log "Can't receive users", err
.then ({data}) ->
  doSomethingWithPosts data.posts
, (err) ->
  console.log "Can't receive posts", err

Can be transformed to something like:

gen ->
    {data: usersData} = yield $http.get '/users/'
  catch err
    console.log "Can't receive users", err
  doSomethingWithUsers usersData.users

    {data: postsData} = yield $http.get '/posts/'
  catch err
    console.log "Can't receive posts", err
  doSomethingWithPosts postsData.posts

Isn’t it cool? But more, result of gen is a promise, so we can write something like:

getUsers = (url) -> gen ->
  {data: {users}} = yield $http.get(url) prepareUser

getPosts = (url) -> gen ->
  {data: {posts}} = yield $http.get(url) preparePosts

gen ->
    users = yield getUsers '/users/'
    posts = yield getPosts '/posts/'
  catch err
    console.log "Something goes wrong", err

   doSomethingWithUsers users
   doSomethingWithPosts posts

So, what gen do:

  1. Creates main promise, which will be returned from gen.
  2. Sends nothing to generator and receives first promise.
  3. If promise succeed, sends result of this promise to the generator. If failed — throws an error to the generator. If we got an exception during .next or .throw — rejects main promise with that exception.
  4. Receives new value from the generator, if the generator is done — resolves main promise with received value, if the value is a promise — repeats the third step, otherwise — rejects main promise.

comments powered by Disqus