News for hackers. Stuff that matters.
Re:Idle Until Urgent by alangpierce on Thursday September 20, 2018 @10:25AM (#18032636)
> Important! While browsers can run input callbacks ahead of queued tasks, they cannot run input callbacks ahead of queued microtasks. And since promises and async functions run as microtasks, converting your sync code to promise-based code will not prevent it from blocking user input!

Wow, my initial reaction while reading was "just use async functions and then then code will naturally allow user input in the middle", good to know that that doesn't work.

Re:Idle Until Urgent by hinkley on Thursday September 20, 2018 @11:16AM (#18033062)
This is slowly turning into a rock in my shoe. You would think a single threaded language would implement some sort of cooperative multitasking scheme. But promises aren’t cooperative, especially in Bluebird (the queue management logic was converted to LIFO a long time ago, although that code still contained comments or variable names that imply the opposite, when last I looked).

I’m tempted to call this the legacy of Brendan “design a language in a week” Eich, but that would let too many other people off the hook.

IMO, webasm can’t happen fast enough.

Re:Idle Until Urgent by paulddraper on Thursday September 20, 2018 @12:55PM (#18033857)
> But promises aren’t cooperative

Promises are cooperative.

You cannot interrupt execution (unlike, say, preemptible threads).

You can only perform other execution when the current execution terminates or yields, e.g. with await.

---

I think you are wanting browser UI events to preempt between Promises/microtasks.

That's fair enough, but saying that Promise aren't cooperative is the wrong description.

Re:Idle Until Urgent by hinkley on Thursday September 20, 2018 @01:45PM (#18034232)
The difference between multitasking and cooperative multitasking is that you can yield the CPU in the middle of a long process. You can do that in Javascript but it involves combining multiple asynchrony APIs in complex ways. Ways you probably don’t want to invite your team to use frequently.

You cannot split a large calculation in the middle by chaining promises to allow even other promises to make progress, let alone event loop processing.

Re:Idle Until Urgent by paulddraper on Thursday September 20, 2018 @03:04PM (#18034739)
> The difference between multitasking and cooperative multitasking is that you can yield the CPU in the middle of a long process.

As long as "can yield" means "able to yield, and able to not yield". More clearly put, the difference between preemptable and cooperative multitasking is

* A preempting scheduler interrupts execution without requiring cooperation from the task https://en.wikipedia.org/wiki/Preemption_(computing)

* A cooperative scheduler interrupts execution only when the task voluntarily yields control https://en.wikipedia.org/wiki/Cooperative_multitasking

> You cannot split a large calculation in the middle by chaining promises

You can split up large calculations easily:

    (async() => {
        console.log('1');
        await (async() => {});
        console.log('2');
    })();

    (async() => {
        console.log('3');
        await (async() => {});
        console.log('4');
    })();

prints interleaved 1, 3, 2, 4. Calculation split!

---

HTML5 defines a scheduling system ("tasks") on top of ECMAScript's job ("microtask") system.

Thus, browsers have a tiered scheduling system. And DOM events happen at the higher task level.

In a browser, if you want to cooperatively schedule at the task level,

    (async() => {
        console.log('1');
        await new Promise(resolve => setTimeout(resolve));
        console.log('2');
    })();

    (async() => {
        console.log('3');
        await new Promise(resolve => setTimeout(resolve));
        console.log('4');
    })();
You could argue that HTML5 should not have created tiered queues. Perhaps you are correct, though I think it can come in handy.
Re:Idle Until Urgent by hinkley on Thursday September 20, 2018 @04:42PM (#18035292)
I'm not sure what you trying to demonstrate here. It's not the problem I'm talking about. For starters, you have no calculation. You're just running a couple awaits and setTimeouts. Of course those are going to run in 1,3,2,4 order.

The question is how would you make sure 4 happens before 2?

Here's a real world example, from Node: You need to make 3 service calls, A B & C, to build a page. Service A is the fastest call, but takes a lot of processing time. Service C is the slowest call, but requires a bit of data from Service B. Since A and B are unrelated, odds are good they're being invoked from completely separate parts of the code.

If you fire A and B, service C won't get called until service A's processing is complete. You could await both A and B, then call C before you start the processing, but you have to turn your code flow inside out to do that, so it only works for trivial applications.

Adding promise chaining to A.process() won't get B's promise to resolve before A's chain finishes resolving. setImmediate() might work in some places, and you might be able to come up with a code pattern that works for your team, but I don't believe it's guaranteed to work everywhere.

Re:Idle Until Urgent by paulddraper on Thursday September 20, 2018 @05:43PM (#18035703)
My initial point what that your terms are muddled.

Your hypothetical could benefit from a preemptable (aka non-cooperative) scheduler, which can forcibly interrupt A, to allow C to start.

A cooperative scheduler (which is what JS has) is at the mercy of A to properly yield.

---

As for how to yield on the macro- or microtask queue of your choice, they are the same difficulty to write.

   // HTML5, Node.js
   await new Promise(resolve => resolve());
   await new Promise(resolve => setTimeout(resolve, 0));

   // Node.js
   await new Promise(resolve => resolve());
   await new Promise(setImmediate);
You're correct that setTimeout and setImmediate are not guaranteed to work on all ES runtimes, because they are HTML5 and Node.js specific additions. (As is the entire concept of a separate macrotask queue, which you dislike so much.)
Re:Idle Until Urgent by Xichom2k on Thursday September 20, 2018 @12:02PM (#18033453)
You can yield back to the UI event loop in an async function through

   await new Promise((resolve) => setImmediate(resolve))
or similar constructs using setTimeout or requestAnimationFrame
Re:Idle Until Urgent by paulddraper on Thursday September 20, 2018 @12:59PM (#18033896)
Correct.

Though setImmediate is specific to IE/Edge.

    setTimeout(resolve, 0)
will allow any queued events/macrotasks to occur, and then immediately resolve.

    requireAnimationFrame(resolve)
will wait until the browser is ready to render another frame, and then resolve.
Re:Idle Until Urgent by Touche on Thursday September 20, 2018 @12:53PM (#18033851)
Yeah this is often misunderstood. Instead of thinking that promises make your code "run later" it's better to think about it as reorganizing still-synchronous code.