

Syncify.js – Eliminates the need for callbacks in the browser - dgellow
https://github.com/aldonline/syncify

======
drostie
There's something that I'm missing here. Can anyone who is involved with this
project clarify this example:

    
    
        function getFullName( id ){
            return ajax( "/user/" + id + "/name" ) + " " + ajax( "/user/" + id + "/lastname" )
        }
    

I've read through the source and it mentions blocking etc. but I didn't see
quite what I was looking for.

So here's the deal: at one point I was working on laziness library to treat
promises as lazy values, and the problem that I ran into was that JS doesn't
let you overload the operators `+`, `-`, etc. so that the API needs to export
things like `Lazy.plus(p1, p2, p3)`. By itself that's not so bad -- it even
makes everything look lispy in a strangely C syntax -- but it was sufficiently
heavy that I kind of abandoned the project.

So from my understanding, the `+` can only work if the `ajax()` calls now
block _the global browser JS thread_. Is that true? Does the above function
even work synchronously, if I call it?

Of course, to _actually_ use this function we have to use `Syncify.revert`
which converts it back into a callback-based function. Does Syncify.revert
have to somehow parse the function? How does it "stick its own context" into
the function that it's calling without something complicated like dynamic
variable scope? Or did you find a way to hack dynamic scope into JS?

~~~
virulent
It seems to depend on reactivity[1], hence it doesn't block.

Here's an example:
[https://jsfiddle.net/k61y5sLo/2/](https://jsfiddle.net/k61y5sLo/2/)

    
    
        Pre-callback
        Evaluating getCallbackText()
        Post-callback
        callbackTest => cb("Alice")
        Evaluating getCallbackText()
        callbackTest => cb("Bob")
        Evaluating getCallbackText()
        Callback returned: Alice Bob
    

[https://github.com/aldonline/reactivity](https://github.com/aldonline/reactivity)

~~~
zackmorris
Thanks for the example. Just to clarify, it appears that you used
syncify.revert(getCallbackText) to to create a callback from a synchronous
function in order to log the final result.

So it looks like Syncify.js evaluates the parent getCallbackText() function
each time that one of its child callbackTest("Alice") or callbackTest("Bob")
functions fire. Conceptually it replaces instantiations of callbackTest() with
their return values. So on the final pass there are no more child functions to
evaluate and it can return the final value.

If this all sounds correct (and please correct me if I'm wrong) then the
tradeoff is some re-run overhead in order to skip having to deal with promises
or yield/generators.

------
adrusi
It seems incredibly fragile, better to just use some kind of compile-to-js
language. IcedCoffeeScript, Clojurescript, Gopherscript and more all make it
possible to avoid callbacks in a cleaner way, with fewer restrictions and
probably better performance. Babel has experimental support for es7 async
functions, if you really want to write javascript code.

~~~
voxtex
I've found using promises with generators yields code very similar to
async/await, without relying on experimental features.

[https://github.com/tj/co](https://github.com/tj/co)

or

[https://github.com/petkaantonov/bluebird/blob/master/API.md#...](https://github.com/petkaantonov/bluebird/blob/master/API.md#promisecoroutinegeneratorfunction-
generatorfunction---function)

~~~
randall
Also there's this:
[https://github.com/yortus/asyncawait](https://github.com/yortus/asyncawait)

------
NDizzle
Excuse my ignorance, but are patterns like the one mentioned in the example in
wide use? Chaining together the results of multiple AJAX calls to form a
single response? (response might not be the correct term - a single return
value)

Wouldn't you just send a request to the server and have it handle things and
spit out the correct response/return value? What's the need for multiple ajax
calls?! I know we're obviously not using this to fetch first and last names -
what's a realistic example for this usage pattern?

~~~
dmak
Very common. RESTful architectures always give rise to this type of pattern.
For example, you fetch a user id, and then you fetch all the blog posts by the
user then you fetch the comments for the blog posts.

~~~
fny
Very common in _poorly designed_ RESTful architectures. Hypermedia-style APIs
take care of this. [0] The performance advantage of lightweight payloads to an
SPA gets trashed by network latency otherwise.

[0]: [http://jsonapi.org/](http://jsonapi.org/)

~~~
Kiro
How would the URL for dmak's example look?

~~~
fny
/users/1/posts?include=user,comments

------
Nemcue
Why not use a Promise library instead?

~~~
dmak
The idea is to improve code composition by creating synchronicity. With
Promises, you generally have to return the promise and then attach a then
method to do something which is contagious and creates tons of dependencies on
promises.

~~~
Nemcue
> something which is contagious

I guess what you're trying to say is that once you adopt Promises a large
portion of your code will have to be tailored to use it.

To which I respond: .. yes, so what? It's better than callbacks. And it's
better than some "magic fairydust" that doesn't make it explicit that you're
doing something async.

~~~
eagsalazar2
Promises are better than callbacks but far worse (in terms of complexity they
create) that synchronous code. This might be fairy dust but it crushes
promises for reducing developer cognitive load. Having said that I agree with
the person who commented that async/await is a better way to go (although
sadly not really an option in CS yet).

~~~
monsieurbanana
Sorry could you explain what CS is? I can't think of anything besides computer
science.

~~~
seeekr
CoffeeScript! :-) One of those neat compiles-to-JS languages!

Edit: And the "problem" with async/await and CoffeeScript is that the former
is currently available in various JS transpilers but you can't use one of
those and CoffeeScript at the same time.

------
mbrock
When I see a website for a tricky language thing like this, within five
seconds I'm wondering "okay, what EXACTLY does the thing do?"

Sometimes the website answers that. This one doesn't, and so I'm absolutely
not going to use it, because I don't understand it.

~~~
amelius
My guess: the system keeps calling your function, and throws an exception
whenever an asynchronous call needs to be made which was not yet handled. This
exception is then caught inside the system to mark the asynchronous call as
being done, and the next iteration starts.

~~~
HelloThereHuman
How about actually giving it a minute or two to form an educated opinion
instead of spewing out cynical imaginations?

------
dmak
I get that it returns a wrapper, but the functions like map and toUpperCase
would have to wait for the asynchronous callback somehow. How does this work?

~~~
igl
It basicly a wrapper around Promises.

Nothing of your sync functions work until after you do syncify.revert which
gives you a function taking a CALLBACK.

~~~
dmak
I get promises, but in this example:

function getFullName( id ){ return ajax( "/user/" \+ id + "/name" ) + " " \+
ajax( "/user/" \+ id + "/lastname" ) }

How does it know to to wait for both ajax functions before returning the
correct value? Am I misunderstanding something?

~~~
zackmorris
See my response to virulent. I believe it re-runs the parent function multiple
times until all of the child ajax functions have evaluated to their return
values. This probably explains why the author states that the child functions
must be idempotent (edit: and the code readonly in its entirety to prevent
side effects).

------
EvanYou
This is quite an interesting hack, and I enjoy seeing novel solutions to the
async problem (especially on the browser side, where ES6 is not universally
available yet). But still, I would probably never use this in production,
given that I can just transpile ES7 async/await with babel.

------
Grexception
Reminded me of streamlinejs
([https://github.com/Sage/streamlinejs](https://github.com/Sage/streamlinejs))
but I guess what makes your library better is that there you don't have the
compiler overhead they need, right?

