In node.js, why process.nextTick and runMicroTask runs before setImmediate? - node.js

Here is the code piece:
const fs = require('fs')
process.nextTick(console.log, 1)
setImmediate(() => {
console.log(2)
})
fs.stat('./xxx.js', () => {
process.nextTick(console.log, 3)
queueMicrotask(() => {
console.log(4)
})
setTimeout(() => {
console.log(5)
}, 0)
})
the output: 1 3 4 2 5
I don't understand why 3 and 4 runs before 2, because according to what-is-the-event-loop, when event loop hits the Poll Phase and scheduled with setImmediate, it will not wait but go directly into Check Phase, so setImmediate callbacks should be run first, but obviously it's not, but why?

I guess the fs.stat have something special behavior. You could use other fs function like fs.readFile, then you'll get the 1 2 3 4 5 like below. (Note: your xxx.js file should exist)
const fs = require('fs')
process.nextTick(console.log, 1)
setImmediate(() => {
console.log(2)
})
fs.readFile('./xxx.js', (e, s) => {
process.nextTick(console.log, 3)
queueMicrotask(() => {
console.log(4)
})
setTimeout(() => {
console.log(5)
}, 0)
})

Because setImmediate is not in a I/O callback, its callbacks will be executed in next Eventloop iteration when executed outside of Polling phase.

Related

How to correctly structure a nested node-fetch request to avoid Promise<pending>?

I'm attempting to
Fetch GET my website (with node-fetch)
Scrape it with Cheerio to get specific posts
Fetch GET from my CMS (with node-fetch) to check if there's already a post with the same time
If the check shows no duplicates, Fetch POST into my CMS
The problem I've run into though, is that I can console.log() the duplicate check result, but when I make a conditional with the result for the Fetch POST request, it always returns the check result as a Promise
I'm not sure how to correctly structure my async .then() calls to correctly check.
Code (edited for simplicity):
fetch('https://www.myblog.com/', { method: 'get' })
.then((res) => {
return res.text()
})
.then((data) => {
const $ = cheerio.load(data)
const siteHeading = $('.post-item')
siteHeading.each((index, element) => {
const title = $(element).find('.entry-title').text()
const desc = $(element).find('.entry-content').text()
const exists = fetch(
'https://mycms.com/api/articles?filters[title][$eq]=' +
title,
{
method: 'GET',
}
)
.then((response) => response.json())
.then((article) => article.data[0])
.then((exists) => {
if (exists) {
// ^exists always shows up as True, console.log(exists) shows Promise<pending>
fetch('https://api.broadband.money/api/news-articles', {
method: 'POST',
body: JSON.stringify({
data: {
title: title ? title : null,
desc: blurb ? blurb : null,
},
}),
})
.then((response) => response.json())
.then((data) => console.log(data))
}
})
})
})
Using .then() makes code difficult to understand especially when they are nested together. You have to keep track of each of then and take care that the brackets are closed at correct place.
Use async await instead. This way you can achieve your objective without so much of nesting:
Example:
const data = await fetch('https://www.myblog.com/'{method:'GET'}).then(res=>res.json)
Here the program will wait for the fetch to get completed and then only it will proceed further.
Remember to use async keyword before function declaration inside which you are using fetch.
Example:
async function functionName()
{
..
const data = await fetch...............
..
}
First, what Marcos advises (combining async/await with .then) is an anti-pattern. Either one or the other must be used.
Second, you use extra .then where you don't need it.
Here:
...
.then((exists) => {
...
Thirdly, the forEach method does not wait for the execution of asynchronous code (although in your case this does not seem to matter).
Here is a small example for understanding:
function each() {
console.log("Start");
const arr = [0, 1, 2, 3];
arr.forEach(async (el) => {
await new Promise((resolve) => {
setTimeout(() => {
resolve(console.log("This log should be after the start", el));
}, 1000);
});
});
console.log("Finish");
}
each();
Output:
Start
Finish
This log should be after the start 0
This log should be after the start 1
This log should be after the start 2
This log should be after the start 3
Instead you should use a "for" loop:
async function loop() {
console.log("Start");
const arr = [0, 1, 2, 3];
for (let i = 0; i < arr.length; i++) {
await new Promise((resolve) => {
setTimeout(() => {
resolve(console.log("This log should be after the start", arr[i]));
}, 1000);
});
}
console.log("Finish");
}
loop();
Output:
Start
This log should be after the start 0
This log should be after the start 1
This log should be after the start 2
This log should be after the start 3
Finish
And finally, it seems to me that replacing this
.then((article) => article.data[0])
.then((exists) => {
with this
.then((article) => {
const exists = article.data[0];
should help. If you provide a working code, I can tell you more.

Async beforeAll() does not finish before beforeEach() is called

In Jest, beforeAll() is supposed to run before beforeEach().
The problem is that when I use an async callback for beforeAll(), Jest doesn't wait for the callback to finish before going on to beforeEach().
How can I force Jest to wait for an async beforeAll() callback to finish before proceeding to beforeEach()?
Minimal reproducible example
tests/myTest.test.js
const connectToMongo = require('../my_async_callback')
// This uses an async callback.
beforeAll(connectToMongo)
// This is entered before the beforeAll block finishes. =,(
beforeEach(() => {
console.log('entered body of beforeEach')
})
test('t1'), () => {
expect(1).toBe(1)
}
test('t2'), () => {
expect(2+2).toBe(4)
}
test('t3'), () => {
expect(3+3+3).toBe(9)
}
my_async_callback.js
const connectToMongo = async () => {
try {
await mongoose.connect(config.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true
})
console.log('Connected to MongoDB')
} catch (err) {
console.log(`Error connecting to MongoDB: ${err.message}`)
}
}
module.exports = connectToMongo
UPDATE: As the accepted answer helpfully points out, Jest actually does wait for beforeAll to finish first, except in the case of a broken Promise chain or a timeout. So, the premise of my question is false. My connectToMongo function was timing out, and simply increasing the Jest timeout solved the problem.
The problem is that when I use an async callback for beforeAll(), Jest doesn't wait for the callback to finish before going on to beforeEach().
How can I force Jest to wait for an async beforeAll() callback to finish before proceeding to beforeEach()?
TLDR
The short answer is that Jest does wait for an async beforeAll() callback to finish before proceeding to beforeEach().
This means that if beforeEach() is running before something that should run in beforeAll() then the Promise chain must be broken or the beforeAll function is timing out.
Queue Runner in Jest
All of the beforeAll, beforeEach, test, afterEach, afterAll functions associated with a test are collected in queueableFns and are chained on these lines in queueRunner.ts:
const result = options.queueableFns.reduce(
(promise, fn) => promise.then(() => mapper(fn)),
Promise.resolve(),
);
So Jest starts with a resolved Promise and chains every function, in order, to the Promise chain with .then.
This behavior can be seen with the following test:
const order = [];
// first beforeAll with async function
beforeAll(async () => {
order.push(1);
await new Promise((resolve) => { setTimeout(resolve, 1000); });
order.push(2);
});
// first beforeEach with done callback
beforeEach(done => {
order.push(4);
setTimeout(() => {
order.push(6);
done();
}, 1000);
order.push(5);
});
// second beforeEach
beforeEach(() => {
order.push(7);
});
// second beforeAll
beforeAll(() => {
order.push(3);
});
it("should run in order", () => {
expect(order).toEqual([1, 2, 3, 4, 5, 6, 7]); // SUCCESS!
});
Broken Promise Chain
If beforeEach is running before something that should run in beforeAll then it is possible the Promise chain is broken:
const order = [];
// does not return Promise and will break the Promise chain
const func = () => {
setTimeout(() => { order.push(2); }, 1000);
}
const asyncFunc = async () => {
order.push(1);
await func(); // doesn't actually wait for 2 to be pushed
order.push(3);
}
beforeAll(asyncFunc);
beforeEach(() => {
order.push(4);
});
it("should run in order", () => {
expect(order).toEqual([1, 2, 3, 4]); // FAIL: [1, 3, 4]
});
Timeout
...or there is a timeout (note that the timeout will be reported by Jest in the output):
const order = [];
jest.setTimeout(100); // 100ms timeout
const asyncFunc = async () => {
order.push(1);
await new Promise(resolve => { setTimeout(resolve, 1000); }); // times out
order.push(2);
}
beforeAll(asyncFunc);
beforeEach(() => {
order.push(3);
});
it("should run in order", () => {
expect(order).toEqual([1, 2, 3]); // FAIL: [1, 3] and Timeout error
});
If there is async function and callback you can call done. if you want to pass callback inside the async function you are free!
Let me show you;
beforeAll(async (done) => {
await connectToMongo().catch(done) // if there is error it finish with error payload
done(); // it says i am finish. also you can use it on your callback function to say i am done.
})
This happened to me when upgrading to angular 14. Solution was to upgrade zone.js to 0.11.8. Found solution here: https://github.com/angular/angular/issues/45476#issuecomment-1195153212
connectToMongo function is an async function, not a async callback (async function with a function as a parameter ???)
beforeEach will be called when beforeAll is finished, and it still works well.
beforeAll(connectToMongo) will be done right after you call it, this means, it will not wait until the db connect success.
Just wait until connectToMongo done and continue:
beforeAll(async () => { // async function
await connectToMongo() // wait until connected to db
})

Handle async messages in worker threads

We are using NodeJS experimental workers to accomplish some CPU intensive tasks. These tasks are kicked off through messages the parentPort message passing. During the operation of the threads, they need to persist data to a database which is an asynchronous operation backed by promises.
What we are seeing is that the parentPort messages keep being sent to handler function while we doing the asynchronous operations.
An example of the code we are doing:
const { parentPort, Worker, isMainThread } = require('worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
const i = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const x of i) {
worker.postMessage({ idx: x });
}
} else {
parentPort.on('message', async (value) => {
await testAsync(value);
});
}
async function testAsync(value) {
return new Promise((resolve) => {
console.log(`Starting wait for ${value.idx}`);
setTimeout(() => {
console.log(`Complete resolve for ${value.idx}`);
resolve();
if(value.idx == 9) {
setTimeout(() => process.exit(0), 2000);
}
}, 500);
});
}
In the above example we are seeing the Starting wait for ... print before any the the Complete resolve ... messages appear. With the async-await we were expecting the the event handler to wait for the resolved promise before processing the new event. In the real example, the db connection may fail, which throws an exception so we want to ensure that the current message has been fully processed before accepting a new one.
Are we doing anything wrong here?
If not, is there anyway of accomplishing the desired goal of processing the event in order?
It seems that you want to enqueue the messages, and only process one thing at a time.
parentPort.on('message', () => {} is an event listener, when the event is triggered, it won't wait until the previous asyncrhonous operation inside the callback is done.
So, if you trigger 'message' a thousand times, testAsync will be executed a thousand times, without waiting.
You need to implement a queue in the worker, and limit the concurrency. There are multiple promise queue packages in NPM.
I will use p-queue in this example.
const PQueue = require('p-queue');
const { parentPort, Worker, isMainThread } = require('worker_threads');
if (isMainThread) {
const worker = new Worker(__filename);
const i = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (const x of i) {
worker.postMessage({ idx: x });
}
} else {
const queue = new PQueue({ concurrency: 1 }); // set concurrency
parentPort.on('message', value => {
queue.add(() => testAsync(value));
});
}
async function testAsync(value) {
return new Promise((resolve) => {
console.log(`Starting wait for ${value.idx}`);
setTimeout(() => {
console.log(`Complete resolve for ${value.idx}`);
resolve();
if(value.idx == 9) {
setTimeout(() => process.exit(0), 2000);
}
}, 500);
});
}
Now the output will be:
starting wait for 1
complete resolve for 1
starting wait for 2
complete resolve for 2
starting wait for N
complete resolve for N

how to use async.retry within async.race function

I'm using async.race function in nodejs to call 10 functions in parallel, now some of the calls fail and I want to give them a retry, for that I want to use async.retry function.
Here is my code snippet:
async.retry({
interval: 0,
times: 5,
}, this.racemethod, function callback(err, result) {
// do something with the result
});
public async racemethod() {
async.race([
async function f1(callback) {
await agent.TEST("Q.txt");
},
async function f2(callback) {
await agent.TEST("R.txt");
},
async function f3(callback) {
await agent.TEST("S.txt");
},
async function f4(callback) {
await agent.TEST("T.txt");
},
async function f5(callback) {
await agent.TEST("U.txt");
},
async function f6(callback) {
await agent.TEST("V.txt");
},
async function f7(callback) {
await agent.TEST("W.txt");
},
async function f8(callback) {
await agent.TEST("X.txt");
},
async function f9(callback) {
await agent.TEST("Y.txt");
},
async function f10(callback) {
await agent.TEST("Z.txt");
},
],
// main callback
function callback(err, result) {
// the result will be equal to 'two' as it finishes earlier
});
}
Here is documentation of async.race function: https://caolan.github.io/async/docs.html#race
Here is documentation of async.retry function:
https://caolan.github.io/async/docs.html#retry
I'm new to this, so any suggestions will be appreciated.
Thanks,
Warning: code in answer is for javascript, not typescript - as the code in the question, apart from the public qualifier for racemethod, looks like javascript not typescript
Here's how you'd do it without the async library - the reason you'd want to do it without the async library is because it's quite hard to write code that mixes the async library and promises without breaking something!
Note: you also don't need the async/await ES2016+ keywords, at least, not as often as you use them
And, just to be clear, async library methods like async.race, async.retry - have nothing at all to do with the ES2016+ async/await syntax sugar for Promises
With one of these generic "retry" functions
const retry = fn => () => fn().catch(() => retry(fn));
const limitedRetry = (cont, fn) => fn().catch(err => cont > 0 ? limitedRetry(cont - 1, fn) : Promise.reject(err));
const wait = (time, result) => new Promise(resolve => setTimeout(resolve, time, result));
const limitedRetryDelay = (cont, delay, fn) => fn().catch(err => cont > 0 ? wait(delay).then(() => limitedRetryDelay(cont - 1, delay, fn)) : Promise.reject(err));
Your code becomes (unlimited retries - not recommended)
let winner = Promise.race([
retry(() => agent.TEST("Q.txt")),
retry(() => agent.TEST("R.txt")),
retry(() => agent.TEST("S.txt")),
retry(() => agent.TEST("T.txt")),
retry(() => agent.TEST("U.txt")),
retry(() => agent.TEST("V.txt")),
retry(() => agent.TEST("W.txt")),
retry(() => agent.TEST("X.txt")),
retry(() => agent.TEST("Y.txt")),
retry(() => agent.TEST("Z.txt"))
]);
or- to limit the number of retries (to 10)
limitedRetry(10, () => agent.TEST("Q.txt")),
... etc
or - to limit the number of retries to 10, with a delay of 100ms in between retries
limitedRetryDelay(10, 100, () => agent.TEST("Q.txt")),
... etc
Now, winner is the promise of the race winner
You can either
winner.then(t => {
// t is the winning promise resolved value
});
or, if the above code is inside a function tagged async, change the second line to
let winner = await Promise.race([
... code removed for brevity
]);
// got the winner
and winner will be the winning resolved value

How to put wait in mocha nodejs

CODE -
.then (() => {
console.log("Wait");
setTimeout(function(){console.log("Wait to process")},1500);
this.timeout(2000);
})
.then(() => {
console.log("Get ABC");
return common.getApiData(url)})
Now when i run this code it logs data like -
Wait
Get ABC
Wait to process
(The it waits for the time specified above)
I want to put timeout before calling getApiData method..
Supposing you are using real promises, this is the function you should pass to then:
.then(function (value) {
var p = new Promise ();
setTimeout (function () {
p.resolve(value)
}, 2000)
return p
})
The next then will be called once the promise is resolved.

Resources