8

I'm trying to make (yet another) language that compiles to JavaScript. One of the features I'd like to have is the ability to perform JavaScript's async operations synchronously (not exactly synchronously – without blocking the main thread, of course). Less talk, more examples:

/* These two snippets should do exactly the same thing */

// the js way
var url = 'file.txt';
fetch(url)
    .then(function (data) {
        data = "(" + data + ")";
        return sendToServer(data);
    }).then(function (response) {
        console.log(response);
    });

// synchronous-like way
var url = 'file.txt';
var response = sendToServer("(" + fetch(url) + ")");
console.log(response);

What is the most elegant way to compile it back to JS?

The criteria, sorted by importance:

  1. Performance
  2. Backwards compatibility
  3. Compiled code readability (not really important)

There are several possible ways to implement it, those three came to my mind:

  1. Using Promises:

    • f(); a( b() ); would turn into
    • f().then(function(){ return b() }).then(function(x){ a(x) });
    • pros: relatively simple to implement, polyfillable back to ES3
    • cons: creating a new function and a Proxy instance for every function call
  2. Using generators:

    • f(); a( b() ); would turn into
    • yield f(); tmp = yield b(); yield a( tmp );
    • pros: nicer javascript
    • cons: creating proxies, generators and iterators on every step, also not polyfillable
    • EDIT: in fact they are polyfillable using another recompiler (eg. Regenerator)
  3. Using synchronous XMLHttpRequest:

    • request a server script which waits until another call from somewhere else in the code
    • pros: de facto no changes in the code, super-easy to implement
    • cons: requires server script (=>no portability, browser-only); furthermore, synchronous XMLHttpRequest blocks the thread so it does not work at all
    • EDIT: It actually would work inside a Worker

The main problem is that one can't tell if a function is asynchronous until runtime. That results in the two solutions that at least do work being a lot slower than pure JS. So I ask:

Are there any better ways to implement?

From answers:

Await keyword (@MI3Guy)

  • C# way (which is good!)
  • introduces a new keyword (which can be disguised as a function (1st-class-citizen) that eg. throws if the programmer tries to pass it as an argument)
  • How to know that function is asynchronous? No other keywords!
    • Every function that contains await keyword becomes asynchronous?
    • When the code gets to await, it returns a promise? Else treats as an ordinary function?
  • 11
    synchronously (without blocking the thread, of course) You keep using that word. I do not think it means what you think it means, because synchronously means "blocking the thread". – Mason Wheeler Apr 8 '15 at 16:56
  • 2
    Read more about continuations and continuation passing style – Basile Starynkevitch Apr 8 '15 at 17:08
  • 1
    @MasonWheeler: I assume the op means he wants to use synchronous-style syntax. – Brian Apr 8 '15 at 17:13
  • I won't call this an answer, but what you're attempting sounds a bit like what C# does with its async functions. I feel like the most consistent method with return values would be the use of promises, and yes, this would involve a lot of anonymous functions. You could try to find articles on how C# code trans-compiles for reference; it's probably just as inefficient in some ways. – Katana314 Apr 8 '15 at 17:26
  • 2
    f(); a( b() ); would turn in to f().then(b).then(a); You don't enclosing functions. – thefourtheye Apr 10 '15 at 16:18
3

Assuming the language is dynamically typed, I can see two different ways this could be handled without knowing at compile time if a call is async.

One approach would be to assume every call could be async. However, this would be incredibly inefficient and produce a lot of extra code. This is essentially what you are doing in option 2.

Another option is to use fibers. Fibers are like lightweight threads that are scheduled by the program. The fiber itself decides when to give up control. However, these require operating system support. As far as I can tell, the only way to use fibers in JavaScript is within node.js. My guess is that if you are compiling to JS that the goal (or at least a goal) is to run on the browser which rules out this option. This would be a more lightweight version of option 3.

Honestly, I would consider adding a keyword like C#'s await. This has a number of advantages in addition to specifying that a function is async. Functions can be called to produce futures without awaiting them immediately. This allows for multiple async operations to occur at once rather than in sequence. Additionally, it is better to be explicit about what operations are async because other code can run while the operation is taking place. If async is automatic, it may be surprising when some data you are using is modified by an external piece of code.

In C# within a method marked as async, the return statement is used to return the value that will be inside the future, not the future itself. Note, however, that the async modifier is not really part of the method signature. await can be used with any method that return a future.

If you don't wish to add an async modifier for functions, you could decide (as you stated) that any function with an await is treated like it has an implicit async modifier. So even if a function has an await, but returns before reaching it, the function should still return a future. I would also recommend exposing some way to convert a value into a completed future that contains the value, especially if there is no way to have that done implicitly by adding the async modifier.

  • My original goal was to create a totally consistent language with no structures that couldn't be reused (eg. if there's if(){} else{}, the programmer could also define a similar structure like ifZero(){} elseWhile(){}). But it seems I have to introduce a global await function that throws (or at least returns undefined) if the programmer tries to assign its value to a variable (functions are 1st class citizens). – m93a Apr 9 '15 at 6:52
  • Updated the question (the very bottom). Would you please tell me what do you think about the questions there? Thanks. – m93a Apr 9 '15 at 8:21
  • I added more details based on your updates. Let me know if you meant something different. – MI3Guy Apr 9 '15 at 22:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.