In Node.js design patterns untying zalgo, why is the asynchronous path consistent?

In a large book, I am reading NodeJs design patterns I see the following example:

 var fs = require('fs'); var cache = {}; function inconsistentRead(filename, callback) { if (cache[filename]) { //invoked synchronously callback(cache[filename]); } else { //asynchronous function fs.readFile(filename, 'utf8', function(err, data) { cache[filename] = data; callback(data); }); } } 

then

 function createFileReader(filename) { var listeners = []; inconsistentRead(filename, function(value) { listeners.forEach(function(listener) { listener(value); }); }); return { onDataReady: function(listener) { listeners.push(listener); } }; } 

and its use:

 var reader1 = createFileReader('data.txt'); reader1.onDataReady(function(data) { console.log('First call data: ' + data); 

The author says that if an item is in the cache, the behavior is synchronous and asynchronous if it is not in the cache. I'm fine with that. he then goes on to say that we must be either synchronous or asynchronous. I'm fine with that.

I do not understand that if I take the asynchronous path, then when this line is var reader1 = createFileReader('data.txt'); will be executed, it is impossible to finish reading the asynchronous file and thus the listener will not be registered in the next line, which is trying to register it?

+7
javascript asynchronous zalgo
source share
4 answers

JavaScript will never interrupt a function to run another function.

The "file was read" handler will be queued until the JavaScript event loop is free.

+1
source share

An asynchronous read operation will not call its callback or trigger descent events until the current tick of the event loop is set, therefore, the synchronization code that the event listener registers will be executed first.

0
source share

Yes, I feel the same when I read this part of the book. "inconsistentRead looks good"

But in the following paragraphs, I will explain the potential error that such synchronization / asynchrony functions can use when used (so this also could not get through).

As a result, the following was used in the sample:

In event loop 1:

reader1 created, reason: "data.txt" is not cached yet, it will respond async in another N. event loop

some callbacks subscribe to reader readiness1. And it will be called in cycle N.

In the N: "data.txt" event loop, it reads, and this notification is cached, so callbacks with readers1 are called.

In the event loop X (but X> = 1, but X may be before or after N): (maybe a timeout or other schedule of the asynchronous route) reader2 is created for the same file "data.txt"

What happens if: X === 1: the error can be expressed in the wrong way, because the result of data.txt will try to cache twice, the first read, faster, will be defeated. But reader2 will register its callbacks before the asynchronous response is ready, so they will be called.

X> 1 and X <N: the same thing happens as X === 1

X> N: the error will be expressed as described in the book:

You create reader2 (the answer for it is already cached), onDataReady is called, because the data is cached (but you have not signed any subscriber yet), and after that yo sign the callbacks with onDataReady, but this will not be called again.

X === N: Well, this is an extreme case, and if the first part of reader2 will go the same as X === 1, but if it starts after the "data.txt" section with readiness for inconsistency, then it will happen like this same as for X> N

0
source share

I think the problem can also be illustrated with a simpler example:

 let gvar = 0; let add = (x, y, callback) => { callback(x + y + gvar) } add(3,3, console.log); gvar = 3 

In this case, callback is called immediately inside add , so changing gvar subsequently has no effect: console.log(3+3+0)

On the other hand, if we add asynchronously

 let add2 = (x, y, callback) => { setImmediate(()=>{callback(x + y + gvar)})} add2(3, 3, console.log); gvar = 300 

Since the execution order, gvar=300 is executed before the asynchronous call to setImmediate , so the result becomes console.log( 3 + 3 + 300)

In Haskell, you have a pure vs monad function that is similar to the async functions that execute later. In Javascript, they are not explicitly declared. Thus, this "delayed" executable code can be difficult to debug.

0
source share

All Articles