0

I'm working with a modified version of this example for writing a helper for vaguely async/await syntax. Here's the code in TypeScript:

export class Async {
  public static do(makeGenerator) {
    let generator = makeGenerator.apply(this, arguments);

    try {
      return handle(generator.next());
    } catch (error) {
      return Promise.reject(error);
    }

    function handle(result) {
      if (result.done) {
        return Promise.resolve(result.value);
      }

      return Promise.resolve(result.value)
        .then(nextResult => {
          return handle(generator.next(nextResult));
        })
        .catch(error => {
          return handle(generator.throw(error));
        });
    }
  }
}

The usage is meant to be like:

Async.do(function* () {
  let someResult = yield somePromise();
  // Don't continue until the promise resolves
  someDependentOperation(someResult);
});

That all works just fine.

Where it falls down is if I try to return Async.do(...). I'd thought from inspecting the code that this should return a Promise object, but this apparently isn't the case. Async.do returns immediately, and the result is undefined. Is there something I'm missing about the way generators work?

Heretic Monkey
  • 11,687
  • 7
  • 53
  • 122
Alec
  • 2,432
  • 2
  • 18
  • 28
  • wait, `public` is a valid keyword since when? Also, I vaguely recall `return` within a `try/catch` not actually returning the object. Try assigning it to a variable and then returning that outside the `try/catch` block – Patrick Roberts Jul 03 '17 at 18:59
  • Sorry! It's in typescript. I've edited it. I'll try the try catch thing in a bit. – Alec Jul 03 '17 at 19:06
  • Ah, gotcha. Thanks, I thought I was crazy for a second. – Patrick Roberts Jul 03 '17 at 19:07
  • 3
    I cannot reproduce this: I get a promise. Can you provide a fiddle that demonstrates that you get `undefined`? – trincot Jul 03 '17 at 19:15
  • You are probably getting a promise back, but your code isn't waiting for completion of it. I think you still need to use then on the calling code. – Mike Feltman Jul 03 '17 at 19:21
  • 3
    `do` definitely returns a promise since `handle` and `Promise.reject` both return promises. However, the function you are passing to `do` doesn't return anything, so the promise that is returned will resolve to `undefined`. – Felix Kling Jul 03 '17 at 19:23
  • 2
    Hmm I can't seem to reproduce in a fiddle. I think this must be something wrong with some other code. Thanks for the sense check! – Alec Jul 03 '17 at 19:32
  • Notice that your code [employs the `.then(…).catch(…)` antipattern](https://stackoverflow.com/q/24662289/1048572) instead of properly using `.then(…, …)` like the code on the page you linked. – Bergi Jul 03 '17 at 20:08
  • @Bergi Your link states that using `.then(success, failure)` is an anti-pattern and that `.then(...).catch(...)` is correct - the opposite of what you've written. – Akshat Mahajan Jul 03 '17 at 20:40
  • @AkshatMahajan Then you haven't yet read my answer to the linked question – Bergi Jul 03 '17 at 20:48

2 Answers2

0

This works for me, although I rewrote the utility a little bit to return a wrapped function rather than an IIFE:

function awaitify(generator) {
  return function asynced() {
    function awaited(result) {
      const promise = Promise.resolve(result.value)

      return result.done ? promise : promise.then(
        value => awaited(iterator.next(value)),
        error => awaited(iterator.throw(error))
      )
    }

    const iterator = generator.apply(this, arguments)

    try {
      return awaited(iterator.next())
    } catch (error) {
      return Promise.reject(error)
    }
  }
}

class Timer {
  static wait(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
  }
}

awaitify(function* (input) {
  console.log('waiting 3 seconds')
  yield Timer.wait(3000)
  console.log('waiting 2 seconds')
  yield Timer.wait(2000)
  return input
})('test').then(value => console.log(value))

P.S., remove 'test' to allow TypeScript to run this, since asynced() doesn't have any declared arguments.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • This is a fine rewrite (apart from abusing `class` syntax with only static members for a module), but I don't see how this answers the question? – Bergi Jul 03 '17 at 20:06
  • @Bergi I rewrote it from scratch so I could follow what was going on in the OP's code without making any assumptions. The `Timer` isn't really part of the main code, just a wrapper for making promises resolved by `setTimeout`, but my point was just nothing seemed to be wrong with the original code other than not returning the last value. If you want me to delete this I will. – Patrick Roberts Jul 04 '17 at 16:06
  • I'd want you to vote to close the question as off-topic (not reproducible), whether or not you delete your answer is your choice. – Bergi Jul 04 '17 at 16:18
0

I can confirm that this was a problem elsewhere in my code (I'd missed a return off one of my intermediate functions that should have returned a Promise).

Alec
  • 2,432
  • 2
  • 18
  • 28