Generator and Promise Tricks July 18, 2014

When ditching callbacks for promises or generators, life suddenly becomes much easier. You don't have to worry as much about error handling. There are no pyramids of doom. But at first, you'll be confused with how promises and generators work, and finally, you'll be able to use them with expertise!

.map(function* () {})

Some people, including me until recently, see function* () {} as a magical function. They don't correlate function* () {} with regular functions. However, function* () {} is in fact a regular function! The only real difference between function* () {} and function () {} is that the former returns a generator. That means you can pass function* () {} basically anywhere a regular function can go, but just make sure you realize that a generator is returend.

For example, to execute in parallel, you might do something like this:

co(function* () {
  var values = [] // some values
  yield values.map(function (x) {
    return somethingAsync(x)
  })
})()

function* somethingAsync(x) {
  // do something async
  return y
}

When you can just do:

co(function* () {
  var values = []
  yield values.map(somethingAsync)
})

Similarly, you can return promises!

co(function* () {
  var values = []
  var mappedValues = yield values.map(toPromise)
})()

function toPromise(x) {
  return new Promise(function (resolve, reject) {
    resolve(x)
  })
}

Wrapping Generators

As such, you could wrap generator functions in regular functions.

function* wrappedFn(x) {
  return yield* somethingAsync(x)
}

Can really just be:

function wrappedFn(x) {
  return somethingAsync(x)
}

And in both cases, you'll be able to do yield* wrappedFn().

.then = (resolve, reject) =>

One thing I like about the promise specification is that you could literally convert anything into a promise. A promise is just any object with .then().

For example, suppose you have an object stream, and you want .then() to exhaust the stream and return all the objects of the stream.

var toArray = require('stream-to-array')
stream.then = function (resolve, reject) {
  return toArray(this).then(resolve, reject)
}

Now you can simply do stream.then() or even yield stream!

co(function* () {
  var docs = yield stream
})()

Keep in mind that you need to pass resolve and reject to the real .then() method, which you're just proxying to.

Even better, you can create an iterator out of this!

function Iterator() {
  this.i = 0
}

Iterator.prototype.then = function (resolve, reject) {
  return Promise.resolve(this.i++).then(resolve, reject)
}

co(function* () {
  var iterator = new Iterator()
  var i
  while (100 > i = yield iterator) {
    // do something
  }
})()

Basically, just tack on a .then() method to any object you'd like, and you've created a 'yieldable'!