Showing posts with label async. Show all posts
Showing posts with label async. Show all posts

Thursday, December 26, 2019

The Developer who blocked the Event Loop

Recently, our team started working on using Node.js v12 in our v10 app. Luckily, we have unit tests that cover most of the core features. In Node.js 12, one unit test failed. 😱

The test assert that the event loop isn't blocked, even when a large amount of data is processed. Data is processed using async patterns with promises. The unit test add a separate task, triggered with setTimeout, and should be executed during the processing of the large amount of data. But now it is processed after. What? 

Something has changed.


Everything is fine.

Oh no, the code is blocking the event loop.


Here's example code I've written for this post that will behave differently in Node.js 10 and 12.
setTimeout(() => log('first timeout: done!'));

setTimeout(() => {
  processData()
    .then(processData)
    .then(processData)
    .then(processData);
});

// this one should run before all the "processData" calls are done.
setTimeout(() => log('second timeout: done!'));

[full example here]

Breaking changes? 💔
There's a lot of good things in the blog and change log about the Node.js 12 release, but I can't find any breaking API changes. What about Node.js 11? I couldn't find anything about changed event loop behavior there either. 

Promises can block the Event Loop 💤
I found a blog post (linked below) and a GitHub conversation about harmonizing how tasks are prioritized in the browser and in Node.js. Promises and features like setTimeout are since Node.js 11 grouped into micro and macro tasks, with different prioritization in the event loop.

A simple hack 
Here's one way to make the example code behave like it does in Node.js 10. This will make the example code non blocking.

processData()
  .then(() => {
     setTimeout(processData);
});

I hope this post will give you some ideas and help when upgrading the Node.js version. 

Merry Christmas & Happy Coding!
🎅


References
New Changes to the Timers and Microtasks in Node v11.0.0
MacroTask and MicroTask execution order

Wednesday, March 9, 2016

JavaScript async: the future looks promising

In the beginning there was the Pyramids.

Okay, that was not exactly true. We actually had Ajax before that. With ajax, we also got callbacks. Some of the mighty callback pyramids were built and are still standing.

About one year ago, ECMAScript 2015 (aka ES 6) was released and brought native support for Promises to JavaScript. Does that mean that it now is possible to build smaller callback pyramids, instead of the mighty ones? Yes. We also have a standardized and more reliable way of writing async JavaScript code. We can write async code that behaves in a more predictable way when things go wrong.

Here's an example.

// the async consumer code, passing an url and a callback
get('path/to/my/serverside/api', (response) => {
// handle the response here
});


// the callback based ajax library that we use
function get(url, onSuccess, onFail) {
// ajax things here
// pass the result to the provided callback function
onSuccess(result);
}

What if the ajax library at some point would ... go crazy?

onSuccess(result);
onSuccess(result);
onSuccess(result);


The callback will be executed several times and it is out of our control. However, this problem can be solved by using promises. Wrap the ajax library in a promise function and consume the wrapper instead:


// the wrapper
function promiseGet(url) {
    
    return new Promise ((resolve, reject) => {
        get(url, resolve, reject);
    });
}


// consume the promisified version
promiseGet('path/to/my/serverside/api')
.then((response) => {
    // handle the response here
});

The promise will be resolved only once, even if the ajax library goes bananas.


The bus stop
This reminds me of when I was on the bus with the kids a couple of days ago. The little one wanted to press the shiny "bus-will-stop" button. When we got closer to our stop, the kid pressed the button and got the expected feedback (a "bus-will-stop" signal!). That was fun! So, he pressed the button again. But nothing happened. Naturally, he tried a couple of times more. Nothing happened.

Just like the "bus-will-stop" button, a promise is resolved only once. I think the bus driver (and passengers) appreciate the feature.


The async brain?
Async JavaScript code - with or without Promises - isn't always the easiest thing to understand. Promises does not really solve the mismatch between how our brains are wired and the flow of callbacks & thenable functions. We have learned how to write async code, and sometimes even understand it. But wouldn't it be cool if we could write something sequential like this?


var result = get('path/to/my/serverside/api');

console.log(result);


Of course, the code above won't work. 

I will try again, by using some of the magic of generators. A generator enable pause and continue functionality to a function. You can jump back and forth between a generator and the consumer code.


function* myGenerator() {

    var result = yield get('path/to/my/serverside/api');
    console.log(result);

}


That code won't work out of the box either. We need some help from a library. This library (the run function) will help us go back and forth between the generator and the consumer code, and resolve promises.


import run from 'async-runner';

run(function* myGenerator() {

    var result = yield get('path/to/my/serverside/api');
    console.log(result);

});


This will work. (here's the code used in this post)

Sequential async?
Okay, cool. It looks nice and sequential, at least when focusing on the rows within the function. But wait. Will people understand code with funky star functions and weird libraries? There has to be another way. I think the upcoming ECMAScript async & await feature can help us write easy to understand sequential code, without funky star functions or weird libraries.

Let's remove some code from our previous example:

import run from 'async-runner';

run(function* myGenerator() {

    var result = yield get('path/to/my/serverside/api');
    console.log(result);

});


And add some sweet futuristic async & await JavaScript syntax sugar:

async function myAsyncCode() {

    var result = await get('path/to/my/serverside/api');
    console.log(result);

};

With this, I think it actually is possible to use the words "readable" and "async" in the same sentence. Behind the scene, the async & await feature is a combination of Promises and Generators. I think the future of async programming looks very promising. Maybe we don't have to wait for it either. The transpiler Babel has already support for async & await today. Perhaps it is time to go back to the future?


here's the code used in this post