Skip to content

Instantly share code, notes, and snippets.

@bmeck bmeck/gaaaah-y-u-no-this.md Secret
Last active Jan 4, 2016

Embed
What would you like to do?

Assumptions:

  1. async causes it's operand to become a Promise, it will [[call]] the operand if it is define, and start a generator if that is the result.
  2. await acts like a yield that performs Promise.cast(operand)
  • await will resume (.next the generator) when the promise resolves
  • this would allow an async function* to queue async tasks while keeping a linear codebase

What brought this up:

async function* doLogin(req) {
  try {
    var session = await login(req); // wait until the promise resolves
    gotoProfile(session);
  }
  catch (e) {
    printError(e);
    gotoLogin();
  }
  // implicit return undefined
}
// usage
doLogin(req).then(...);

You can think of it in Promises and Generators as similar to:

function doLogin(req) {
  return new Promise(function (f, r) {
    var gen = doLogin_(req);
    gen.next();
    
    function doLogin_*(req) {
      try {
        login(req).then(function (session) {
          gen.next(session);
        })
        .catch(function (e) {gen.throw(e);});
        try {
          var session = yield; // waiting on our promise to finish...
          gotoProfile(session);
        }
        catch (e) {
          printError(e);
          gotoLogin();
        }
        f();
      }
      catch (e) {r(e);}
    }
  });
}
doLogin(req).then(end).catch(die); // handle this as a promise

Allan's simplification by use of new, keeps gen in doLogin_'s scope

function doLogin(req) {
  return new Promise(function (f, r) {
    new doLogin_(req).next();
    
    function doLogin_*(req) {
      var gen = this;
      try {
        login(req).then(function (session) {
          gen.next(session);
        })
        .catch(function (e) {gen.throw(e);});
        try {
          var session = yield; // waiting on our promise to finish...
          gotoProfile(session);
        }
        catch (e) {
          printError(e);
          gotoLogin();
        }
        f();
      }
      catch (e) {r(e);}
    }
  });
}
doLogin(req).then(end).catch(die); // handle this as a promise

Brendan's suggestion to use a library (task.js) - more as a representation of what can be done for simplicity, the library code won't match nicely with source maps / forces having more in the namespace / loading the lib.

spawn(function*()  {
  try  {
    var  session  =  yieldlogin(req);  // wait until the promise resolves
    gotoProfile(session);
  }
  catch  (e)  {
    printError(e);
    gotoLogin();
  }
  // implicit return undefined
});
@spion

This comment has been minimized.

Copy link

commented Jan 23, 2014

Bluebird's Promise.coroutine

var doLogin = Promise.coroutine(function* () {
  // generators do not have references to themselves...
  try {
    var session = yield login(req);
    gotoProfile(session);
  } 
  catch (e) {
    printError(e);
    gotoLogin();
  }  
});
@ghost

This comment has been minimized.

Copy link

commented Jan 23, 2014

It's probably better to separate out the machinery for executing an async function rather than transpiling it out like that.

An async function:

function! doLogin(req) {
  try {
    var session = await login(req);
    gotoProfile(session);
  } catch (e) {
    printError(e);
    gotoLogin();
  }
}

With a simple transformation:

function doLogin(req) {
  return run(_doLogin(req));
}

function* _doLogin(req) {
  try {
    var session = yield login(req);
    gotoProfile(session);
  } catch (e) {
    printError(e);
    gotoLogin();
  }
}

Using this as the plumbing:

function run(gen) {
  function _next(v) { return handle(gen.next(v)); }
  function _throw(e) { return handle(gen.throw(e)); }

  function handle(result) {
    var value = result.value;
    if (!result.done && value && typeof value.then === "function") {
      return value.then(_next, _throw);
    }
    return value;
  }

  return new Promise(function(resolve, reject) {
    Promise.cast(_next()).then(resolve, reject);
  });
}

I made a sweet.js macro that does the transformation: https://gist.github.com/Benvie/8327917

@bmeck

This comment has been minimized.

Copy link
Owner Author

commented Jan 24, 2014

My inclination is to create self contained transpilers, having to introduce an external function call/library makes me wary. If this were intended as a library I would feel ok, but as a syntax/language extension it would be odd to introduce a function like that without some lifting to ensure no name clashes / source maps are valuable still.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.