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) ->
try
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')
else
reject "Value isn't a promise!"
handlePromise generator.next()
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 ->
try
{data: usersData} = yield $http.get '/users/'
catch err
console.log "Can't receive users", err
return
doSomethingWithUsers usersData.users
try
{data: postsData} = yield $http.get '/posts/'
catch err
console.log "Can't receive posts", err
return
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)
users.map prepareUser
getPosts = (url) -> gen ->
{data: {posts}} = yield $http.get(url)
posts.map preparePosts
gen ->
try
users = yield getUsers '/users/'
posts = yield getPosts '/posts/'
catch err
console.log "Something goes wrong", err
return
doSomethingWithUsers users
doSomethingWithPosts posts
So, what gen
do:
- Creates main promise, which will be returned from
gen
. - Sends nothing to generator and receives first promise.
- 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. - 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.