Designing type-safe sync/async mode support in TypeScript

11 points by hongminhee


bakkot

I note that the implementation is repeated, with just some extra awaits scattered in. You can avoid that using generator functions with yield in place of await, like this:

// helpers:
function split(genFn) {
  function runSync(...args) {
    const it = genFn(...args);
    let next = it.next();
    while (true) {
      if (next.done) return next.value;
      next = it.next(next.value);
    }
  }

  async function runAsync(...args) {
    const it = genFn(...args);
    let next = it.next();
    while (true) {
      if (next.done) {
        return await next.value;
      }
      let send = next.value;
      try {
        send = await send;
      } catch (err) {
        next = it.throw(err);
        continue;
      }
      next = it.next(send);
    }
  }

  return { sync: runSync, async: runAsync };
}

// usage:
function* impl(parse) {
  console.log(yield parse('foo'));
}
let impls = split(impl);

impls.sync((x) => x + ' sync'); // synchronously prints
console.log('sync should have already printed here');
await impls.async(async (x) => { await 0; return x + ' async'; }); // asynchronously prints

Might have some trouble getting TypeScript to like this, but it should be doable.

If you want this wrapped up in a library with some bonus features you can use the gensync library, which I have a writeup of here. But the above little snippet is all you really need.

quasi_qua_quasi

Couldn't you just run the command or whatever synchronously? I'm not sure async actually buys you anything here.