Javascript shenanigans



  • I was making a case that there should be a blocking API in a package dedicated to writing tests; you know, when you write a test, you usually imply linear execution with no surprises, so all this multi-tenant event-driven stuff is a burden.

    A "no" response that I've got has got this gem:

    Most node/javascript developers are unfamiliar with any sort of blocking, since it's fundamentally not part of the language.

    And I was, like, :wtf: ?
    And then I imagined this shit smeared all over codebases:

    (2 + 2).then(function(result) { console.log("The result is", result); });
    

    And now I'm fighting an urge to kill these "most node/javascript developers" she refers to with fire.

    I can't even imagine how to reply to that level of inane.


  • sockdevs

    @wft said:

    > Most node/javascript developers are unfamiliar with any sort of blocking, since it's fundamentally not part of the language.

    :cow: :poop:

    blocking is fundamentally part of the language. you have to go out of your way not to block!

    @wft said:

    (2 + 2).then(function(result) { console.log("The result is", result); });

    right, i see where you're going with that shit.



  • I know. I'm just wondering how those hypothetical "javascript programmers not familiar with blocking" even think. Every operation "blocks", if even for a really short while. It's not like it's zero cost.

    I just came across a tweet of a bloke amazed how turning off npm's progress bar sped things up. He probably is not aware that console.log is blocking, and that can show on a slow connection. Dammit, it can show locally.


  • sockdevs

    @wft said:

    ```
    (2 + 2).then(function(result) { console.log("The result is", result); });

    The only way that would work is if somehow `2 + 2` returned a Promise. And if anyone ever implements it that way, I have a heavy percussive tool that will want to converse with them...

  • sockdevs

    @RaceProUK said:

    The only way that would work is if somehow 2 + 2 returned a Promise.

    or if someone shoved a then() into Object.prototype

    and if they did:

    @RaceProUK said:

    I have a heavy percussive tool that will want to converse with them...



  • Actually, any HTTP client-related stuff is so asynchronous you have to work hard to implement it synchronously.

    For example, there's request-sync that links with libcurl, avoiding whatever networking support there is in Node. The whole situation stinks: fs has synchronous counterparts for most of their functions, but networking doesn't.


  • sockdevs

    But the whole point of the asynchronous model is when you're waiting for something like a web request to complete, your program doesn't freeze, and can get on with other stuff.



  • Also, I'm still wondering about these hypothetical node programmers who have learned javascript as their first programming language and don't understand the concept of blocking. What kind of mindset that is.


  • sockdevs

    @wft said:

    fs has synchronous counterparts for most of their functions

    i'd be happier if they didn't really.

    what is it with people and their obsession with blocking IO?

    calculation functions, helper functions, these things i'm okay with having synchronous, but there's no reason to have synchronous IO.


  • sockdevs

    @accalia said:

    what is it with people and their obsession with blocking IO?

    My guess? It's simpler, so they can be lazy and not have to think.



  • There's a problem with most non-GUI client workflows: until they get a response, they have literally no other things to do. Instead of waiting to get the result, I need to get inventive, implement my own band-aid to simulate synchronousness, or tangle a mess of callbacks.



  • @accalia said:

    but there's no reason to have synchronous IO.

    And then you have SQLite which is a flatfile format where you'll run into all kinds of interesting things if you go down the async()-route for Update and Insert.


  • sockdevs

    It's starting to sound like you're using the wrong tool for the job, tbh; if you want synchronous execution, you should really be using a language and framework designed for it, which Node + JS isn't.


  • sockdevs

    @wft said:

    I need to get inventive, implement my own band-aid to simulate synchronousness, or tangle a mess of callbacks.

    this is exactly the problem the Promise/A+ specification was invented to solve.

    as long as you're using NodeJS >=0.12 it's even builtin to the language.



  • @accalia said:

    what is it with people and their obsession with blocking IO?

    Listen kid, I programmed heavily asynchronous shit three years before NodeJS was even conceived, and I've just presented a valid case.

    1. a non-GUI client program doing a single task
    2. an automated test which must be as easy to read and follow as it gets. Really a subset of 1)

  • sockdevs

    @wft said:

    1) a non-GUI client program doing a single task

    a valid use case.

    next question..... why are you doing such a task in a language (okay, library) designed around the idea that all IO should be asynchronous?

    there are other languages that would, barring other design constraints, be better for that purpose. Java and C# would be prime contenders, sitting at the heavyweight end of the spectrum, or possibly Python on the lighter weight end of the spectrum.



  • @accalia said:

    next question..... why are you doing such a task in a language (okay, library) designed around the idea that all IO should be asynchronous?

    We test Angular stuff using Protractor. I really like an idea that the whole stuff is written using one language and doesn't require us all to be polyglots.

    What I don't like is the fact that we've been bitten a few times by the fact that protractor tries to "mask" asynchronousness from you, and this masking (using ControlFlow and shit), as every other abstraction, leaks. Makes maintenance tad more painful than I'd like it to be.



  • Also...

    When a protractor test fails for any reason, stack traces are really useless. Makes it fun to debug. Yay for overtime!


  • sockdevs

    @wft said:

    I really like an idea that the whole stuff is written using one language and doesn't require us all to be polyglots.

    ah, and there would be a design constraint that overrides language selection.

    @wft said:

    We test Angular stuff using Protractor
    hmm not familliar with protractor at all. Mocha is my go to for JS testing, but i don't know how well that works on client side JS (that's what Angular is IIRC)

    @wft said:

    protractor tries to "mask" asynchronousness from you,
    -_-

    i hate it when libraries do that.

    sure it works when you stay on the garden path, and can be a huge boon to you there, but if you put even so much as a toe outside of that garden patht he library laid down for you you just undid all of the benefit of the abstraction and now you have to constantly deal with the leaky (and now untrustworthy) abstraction.


  • sockdevs

    @wft said:

    When a protractor test fails for any reason, stack traces are really useless.

    ah! eactly what happens when i'm running istanbul with mocha for code coverage.

    that's why i have a NPM script that just runs mocha so i can get a good stack trace. it's a shame id protractor doesn't let you do that



  • @accalia said:

    i hate it when libraries do that.

    Me too. Took me a bit to grasp why the stuff like

    someShit.click();
    console.log('I just clicked some shit!');
    otherShit.click();
    console.log('I just clicked another shit!');
    console.log('Returning!');
    return;
    

    executed all in the wrong order. Mind you, I'm not a Javascript programmer primarily, and I don't breathe webdriver documentation, and nobody hinted me at the fact that these click() didn't perform right away but only scheduled actions to be performed.



  • I think what's wrong is that programming is still mostly seen and taught as a single sequence of instructions.

    Programs should be seen fundamentally as a series of objects doing stuff in parallel. Communicating between them is done with queues, events, or similar structures. So if you want to do synchronous reads you just do inputQueue.waitForSomething().


  • sockdevs

    @anonymous234 said:

    Programs should be seen fundamentally as a series of objects doing stuff in parallel. Communicating between them is done with queues, events, or similar structures.

    That's a very OO/event-driven way of looking at programming, which works OK for websites and LOB apps, but perhaps not so much for low-level systems and embedded software.

    I think it's better to think of programming as a toolbox, and you select the correct tools to use based on what you're trying to build.



  • @accalia said:

    That's because you skipped the part of the code that went Number.prototype.then = function(f) { setTimeout(f.bind(null, this), 0); }.



  • @RaceProUK said:

    The only way that would work is if somehow 2 + 2 returned a Promise. And if anyone ever implements it that way, I have a heavy percussive tool that will want to converse with them...

    Oh, would you look at the weather...



  • @Zecc said:

    f(this);

    I need to find a way to work this into my code.


  • sockdevs

    @Zecc said:

    Number.prototype.then = function(f) { setTimeout(f.bind(null, this), 0); }.

    wait. no. wrong reaction. hold on.....

    There we go....



  • @wft said:

    blocking

    Presumably this individual is referring specifically to asynchronous operations, or they just don't know what 'blocking' means.

    @wft said:

    (2 + 2).then(function(result) { console.log("The result is", result); });

    Please.

    Stop.

    You're hurting us.

    @wft said:

    We test Angular stuff using Protractor.

    I've recently been getting into the depths of e2e. I've been digging up some gems.

    [code]
    {
    // ...
    payment_add:"payment_add",
    payment_own_edit: "payment_own_edit",
    payment_own_delete:"payment_own_delete",
    payment_all_edit:"payment_all_edit",
    payment_all_delete: "payment_all_delete",
    payment_all_approve_deny: "payment_all_approve_deny",
    payment_all_auto_approve: "payment_all_auto_approve",
    payment_all_authorise: "payment_all_authorise",
    payment_own_auto_authorise: "payment_own_auto_authorise",
    // ...
    }
    [/code]

    Hardcoded permissions which should be coming from the API. I don't know why they're there.



  • Blocking IO is fine if you have threads. The reason Node needs all this shit is that it has no proper threads.

    I'm kind of annoyed at the new .net stuff (Like HttpClient) only implementing async. If I'm already in a worker thread shoving data into an API, having to use async HttpClient is a PITA which delivers no benefit over ancient old WebClient.


  • sockdevs

    @blakeyrat said:

    I'm kind of annoyed at the new .net stuff (Like HttpClient) only implementing async.

    var client = new HttpClient();
    var req = client.GetAsync("https://what.thedailywtf.com/");
    req.Wait();
    var body = req.Result;
    

    not too bad, and even if i add sanity checks and error code checks....

    and yes it does compile:

    you can even write a neat extension method to Task<T> that gives you a WaitResult<T>() method so you can do that all in one line and not have to worry about the req variable.



  • @blakeyrat said:

    Blocking IO is fine if you have threads.

    I'd take blocking and threads over event queues or callbacks any day.

    Look, I have blocking and threads. And it runs in a web browser. It even compiles in a web browser.



  • @accalia said:

    not too bad,
    And req.Result automatically calls req.Wait() if needed.@accalia said:
    what is it with people and their obsession with blocking IO?

    calculation functions, helper functions, these things i'm okay with having synchronous, but there's no reason to have synchronous IO.

    When I added the version banner to SockBot 0.xx, I made it synchronous so that it'd have a chance to print before some fatal error blew up the process, defeating the whole point. And yes, I could have done it the "proper" way, but you would have driven down here and murdered me if I had function main() { printVersion().then(restOfMain);}...


  • sockdevs

    @TwelveBaud said:

    And req.Result automatically calls req.Wait() if needed.

    huh.

    good to know you don't even need the extension method!

    even shorter version:

    var client = new HttpClient();
    var body = client.GetAsync("https://what.thedailywtf.com/").Result;
    

    @TwelveBaud said:

    When I added the version banner to SockBot 0.xx, I made it synchronous so that it'd have a chance to print before some fatal error blew up the process, defeating the whole point.
    hmm. i remember that, and i remember having a real hard time with that from a concept point of view.

    it never did sit well with me, even though i did understand your reasoning.

    @TwelveBaud said:

    you would have driven down here and murdered me if I had function main() { printVersion().then(restOfMain);}...
    nah.

    i may have yelled a bit, but moider? no, there would be no call for that


  • Discourse touched me in a no-no place

    @Rhywden said:

    you have SQLite which is a flatfile format

    It doesn't look very much like that to me, but then I've just checked a more definitive source. Flat file formats are ones where the typical usage pattern is to open and read from the beginning to the end; text files are classic examples of flat files, as are (most) images. What's more, there's quite a bit of internal structure in the SQLite DB format.



  • That actually isn't bad at all, I might steal that.

    @TwelveBaud said:

    And req.Result automatically calls req.Wait() if needed.

    I honestly did not know that.

    Ok so I guess I'm complaining about nothing.


  • sockdevs

    @accalia said:

    ```
    var client = new HttpClient();
    var body = client.GetAsync("https://what.thedailywtf.com/").Result;

    A fair attempt, but it's prone to deadlocks in some scenarios. What you really want is this:
    

    var client = new HttpClient();
    var body = Task.Run(async () => await client.GetAsync("https://what.thedailywtf.com/")).Result;

    And yes, I have had to use this solution in an ASP.NET MVC project.


  • @RaceProUK said:

    A fair attempt, but it's prone to deadlocks in some scenarios.

    Like what scenarios?

    @RaceProUK said:

    var body = Task.Run(async () => await client.GetAsync("https://what.thedailywtf.com/")).Result;

    Aren't you just wrapping a Task in another Task? What's the point of this "fix"? What scenario does it address?


  • sockdevs

    @blakeyrat said:

    That actually isn't bad at all, I might steal that.

    have at it. i hereby release that code sample into the public domain, without let or hindrance, for the betterment of all sapient kind. Further, were such public domain dedication is not lawful hereby release all copyright claim to such code, for myself and any of my heirs that may apply.


  • sockdevs

    @blakeyrat said:

    Like what scenarios?

    The specific one I've run into is when calling an async operation in BeginExecuteCore, which cannot be overloaded to be async, so you have to run it sync somehow. However, simply calling .Result or .Wait() does something weird when the async operation completes, and it deadlocks. It's something to do with thread context, but I can't find the page I found before that explains it; if I do, I'll link it here.

    And before you suggest RunSynchronously(), I tried that; it throws an exception.

    @blakeyrat said:

    Aren't you just wrapping a Task in another Task? What's the point of this "fix"? What scenario does it address?

    Yes, to avoid deadlocks, and scenarios where the thread contexts cause a deadlock. And it's not just the above where it happens; it happens in anything with a UI thread too.


  • sockdevs

    @RaceProUK said:

    prone to deadlocks in some scenarios.

    hmm? I know it can deadlock if you happen to call it in a UI thread, but that';s more to do with how weird UI threads are than HttpClient itself.

    how can it deadlock in a non-UI thread?


  • sockdevs

    TBH, no idea, but it does. Maybe it's just a weirdness that ASP.NET MVC shares with e.g. WPF.

    Edit: Ah, @TwelveBaud knows ;)



  • Oh, ASP.NET. That's different.It has a non-default "synchronization context", which makes the TPL task scheduler "prefer" that request's thread to run tasks. I'd guess it's waiting on the thread to become active so it can pass the result but can't because the thread is waiting for the result.

    That sounded a lot smarter before I said it. WTF, Microsoft?



  • Ultimately, this all comes down to the "JavaScript / .NET / Java / Etc." allow functions to have two colors:

    I’ll rant about a language I just made up. A strawman whose sole purpose is to be set aflame.
    I know, this seems pointless right? Trust me, by the end, we’ll see whose face (or faces!) have been painted on his straw noggin.

    A new language

    Learning an entire new (crappy) language just for a blog post is a tall order, so let’s say it’s mostly similar to one you and I already know. We’ll say it has syntax sorta like JS. Curly braces and semicolons. if, while, etc. The lingua franca of the programming grotto.
    Except wait. Here’s where our language gets screwy. It has this one peculiar feature:

    1. Every function has a color.

    Each function—anonymous callback or regular named one—is either red or blue. Since my blog’s code highlighter can’t handle actual color, we’ll say the syntax is like:

    blue•function doSomethingAzure() {
      // This is a blue function...
    }
    
    red•function doSomethingCarnelian() {
      // This is a red function...
    }
    

    There are no colorless functions in the language. Want to make a function? Gotta pick a color. Them’s the rules. And, actually, there are a couple more rules you have to follow too:

    2. The way you call a function depends on its color.

    Imagine a “blue call” syntax and a “red call” syntax. Something like:

    doSomethingAzure(...)•blue;
    doSomethingCarnelian()•red;
    

    If you get it wrong—call a red function with •blue after the parentheses or vice versa—it does something bad. Dredge up some long-forgotten nightmare from your childhood like a clown with snakes for arms under your bed. That jumps out of your monitor and sucks out your vitreous humour.
    Annoying rule, right? Oh, and one more:

    3. You can only call a red function from within another red function.

    You can call a blue function from with a red one. This is kosher:

    red•function doSomethingCarnelian() {
      doSomethingAzure()•blue;
    }
    

    But you can’t go the other way. If you try to do this:

    blue•function doSomethingAzure() {
      doSomethingCarnelian()•red;
    }
    

    Well, you’re gonna get a visit from old Spidermouth the Night Clown.
    This makes writing higher-order functions like our filter() example trickier. We have to pick a color for it and that affects the colors of the functions we’re allowed to pass to it. The obvious solution is to make filter() red. That way, it can take either red or blue functions and call them. But then we run into the next itchy spot in the hairshirt that is this language:

    4. Red functions are more painful to call.

    For now, I won’t precisely define “painful”, but just imagine that the programmer has to jump through some kind of annoying hoops every time they call a red function. Maybe it’s really verbose, or maybe you can’t do it inside certain kinds of statements. Maybe you can only call them on line numbers that are prime.
    What matters is that, if you decide to make a function red, everyone using your API will want to spit in your coffee and/or deposit some even less savory fluids in it.
    The obvious solution then is to never use red functions. Just make everything blue and you’re back to the sane world where all functions have the same color, which is equivalent to them all having no color, which is equivalent to our language not being entirely stupid.
    Alas, the sadistic language designers—and we all know all programming language designers are sadists, don’t we?—jabbed one final thorn in our side:

    5. Some core library functions are red.

    There are some functions built in to the platform, functions that we need to use, that we are unable to write ourselves, that only come in red. At this point, a reasonable person might think the language hates us.

    No matter what, you’ll have to think about color constantly. It will be the sand in your swimsuit on the beach vacation of development.

    Not that Go and Lua don't suffer from other problems (call/cc is not a panacea either) - they just don't happen to suffer from this one


  • sockdevs

    @RaceProUK said:

    The specific one I've run into is when calling an async operation in BeginExecuteCore, which cannot be overloaded to be async, so you have to run it sync somehow. However, simply calling .Result or .Wait() does something weird when the async operation completes, and it deadlocks. It's something to do with thread context, but I can't find the page I found before that explains it; if I do, I'll link it here.

    oh. weird.

    also neat.

    so we have this then:

        public static class Extensions
        {
            public static T GetResult<T>(this Task<T> req)
            {
                return Task.Run(async () => await req).Result;
            }
        }
    
    var client = new HttpClient();
    var body = client.GetAsync("https://what.thedailywtf.com/").GetResult();
    


  • I just skimmed this, but it looks like the completion of httpresult gets scheduled to the same thread that the request was launched on, so if you're blocking on that thread, the response handler will never run. Task.run puts it in another thread.


  • sockdevs



  • Seems you didn't quote important part, I have no idea what that dude is yapping about. Now I have to read the damn post, sigh.



  • Ultimately, he's arguing for call/cc in the style of Go, Ruby, and Lua.

    That has other problems, since any invocation anywhere can interrupt you, and if you need something to be atomic then you need to do additional work to indicate that to the language.



  • @wft said:

    you know, when you write a test, you usually imply linear execution

    No, I don't know. If your tests fail in a random order in parallel, they are bad tests.



  • I've read his post now, and I actually agree with him. Being a c# programmer first does have a shitload of advantages. I ♥ c♯


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.