maxtaco / tamejs

JavaScript code rewriter for taming async-callback-style code

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Impossible to await and return inside a function?

felixge opened this issue · comments

Is it possible to await and return a value from inside a function? For example:

function sleep(cb) {
  await setTimeout(defer(), 1000);
  cb(null);
}

var r = sleep(function() {
  console.log('done');
});
console.log('r', r);

Is it possible to return a value from the sleep function here? This is really useful when writing fluid interfaces that return this; from every function.

--fg

Hi Felix, Thanks for the bug report. This is getting to a deeper semantic issue, it's not just a bug. Returning a non-void from a tamed function (one with await) is weird. For instance, there's not a great way to make the following work:

void foo (cb) {
   var n  = 10;
   await setTimeout (defer (), 10);
   n = 11;
   cb ();
   return n;
}

await { var i = foo (defer ()); }

Because the value i in the caller is assigned before the timeout (that's the first time that foo "returns" control back to the caller, though it's implicit).

Now you could do something nasty like this:

void foo (cb) {
   DEFAULT_RETURN_VALUE { var n; }
   n = 10;
   await setTimeout (defer (), 10);
   n = 11;
   cb();
}

In other words, something to the effect of "the first time we return control to the caller, we want to return the value n, whatever its value is at the time." The programmer would have to keep in mind that this value will be captured at the close of the first await in the function (not obvious).

In C++, we had a DEFAULT_RETURN-type thing for a while, but it was pretty nasty, and we wound up not really needing it. The one time we did/do need it, is when programming connectors, like the timeout connector that we talk about in the README. In that case (very rare in my experience), you do want to await and return a value. The workaround
isn't too bad: https://github.com/maxtaco/tamejs/blob/master/lib/connectors.tjs.
That is,

function _timeout (cb, t, res, tmp) {
    var rv = new tame.Rendezvous ();
    var arr;
    tmp[0] = rv.id (true).defer (...arr);
    setTimeout (rv.id (false).defer (), t);
    await { rv.wait (defer (var which)); }
    if (res) { res[0] = which; }
    cb.apply (null, arr);
};

function timeout (cb, t, res) {
    var tmp = [];
    _timeout (cb, t, res, tmp);
    return tmp[0];
};

So you have a helper function _timeout that's deferring, and a wrapper function timeout that's returning. And it's explicit when the value to be returned is captured: tmp[0] = rv.id (true).defer (...arr).

Now, as for fluid interfaces, I haven't really thought too much about those in the tame context... It would definitely get ugly, if you're doing this a lot, to have every function split into two.

Do you have any recommendations for syntax / semantics?

Thanks!

Do you have any recommendations for syntax / semantics?

I was expecting that maybe this could work:

await {
  setTimeout(defer(), 1000);
  return this;
}

That's because all statements inside of an await block should be executed right away, right?

That might get confusing. For instance, what would this do?

function foo ()
{
    await { 
         setTimeout (defer (), 1000);
         return this;
    }
    console.log ("after await");
}

I assume you would still wait it to print out "after await". So return isn't really returning?

Ah shoot, I forgot about that case. I guess for now I don't have a good alternative syntax suggestions, so the workaround function is fine.

I can't really use tame for my main projects anyway due to the line numbering thing. Btw. if you're looking for inspiration on this, kaffeine does similar transforms like tame does which preserve line numbers:

http://weepy.github.com/kaffeine/docs/unwrapping_async_calls.html

--fg

(Closing this for now until I can think of a better idea)