3 ms·
> Important! While browsers can run input callbacks ahead of queued tasks, they cannot run input callbacks ahead of queued microtasks. And since promises and as
by alangpierce 8y ago
> 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.
- hinkley 8y agoThis 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.
- paulddraper 8y ago> 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.
- hinkley 8y agoThe 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.
- paulddraper 8y ago> 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) 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 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.
- hinkley 8y agoI'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.
- Xichom2k 8y agoYou 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
- paulddraper 8y agoCorrect. 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.
- Touche 8y agoYeah 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.