clearInterval of a external function not working - Node.JS - node.js

I have a setInterval function that's been called in another function, and I need to stop it when the proccess is done. I tried to set this setInterval function as a variable and call clearInterval, but the interval keeps running
const createInterval = (visibilityTimeout, startDateTime, message) => {
setInterval(() => {
const currentDateTime = moment().valueOf();
const timeDifference = (visibilityTimeout * 1000) - (currentDateTime - startDateTime);
if (timeDifference >= 600000) {
return;
}
if (timeDifference < 494983) {
const params = {
QueueUrl: 'http://localhost:4566/000000000000/test-queue2',
ReceiptHandle: message.ReceiptHandle,
VisibilityTimeout: visibilityTimeout,
};
sqs.changeMessageVisibility(params, (err, data) => {
if (err) logger.error(err, err.stack);
else logger.info(data);
});
// eslint-disable-next-line no-param-reassign
visibilityTimeout += 300;
}
}, 5000);
};
module.exports = async (message) => {
const startDateTime = moment().valueOf();
const {
noteId,
} = JSON.parse(message.Body);
logger.info(`Processing message [noteId=${noteId}]`);
try {
const note = await TestSessionNote.findById(noteId);
const testSession = await TestSession.findById(note.test_session_id);
logger.info(`Downloading video [key=${testSession.video_key}]`);
const isProcessing = true;
const interval = createInterval(500, startDateTime, message, isProcessing);
await sleep(20000);
clearInterval(interval);
logger.info(`Finished processing message [noteId=${noteId}]`);
} catch (ex) {
await TestSessionNote.update(noteId, { status: 'transcribe_error' });
logger.error(`Error processing message [noteId=${noteId}]`, ex);
}
};
I know that if i create a var test = setInterval(() => {console.log('blabla')}, 500) and call clearInterval(test) it works, but i don't know how can i do this calling a function

I think that you have to return from createInterval function the intervalId and after that it should work.
Can you check what value has your intervalId right now, with your current implementation?
https://developer.mozilla.org/en-US/docs/Web/API/setInterval
"The returned intervalID is a numeric, non-zero value which identifies the timer created by the call to setInterval(); this value can be passed to clearInterval() to cancel the interval."

Related

nodejs/express, Controller send the response before to get the result of function setInterval

the problem is :
In Controller, the response is send before the result of condition into function setInterval
Steps :
Controller run function setInterval
Then send the response to frontend with res.status.json
The function setInterval run
condition of setInterval work and finish
setInterval don't return the resultto controller
the controller have already sent a res...
import checkHealth from "./checkHealth.js";
async function checkHealthInterval(labelData) {
console.log("labelData==>", labelData);
let resultInterval = { health: "", message: "" };
let i = 1;
let interval;
interval = await setInterval(async () => {
let healthCheckers = await checkHealth();
let healthCheckerStatus;
if (labelData== "example1") {
healthCheckerStatus = healthCheckers.statusInstanceGlobal;
console.log(
"healthCheckerStatus value from tenant1 : ",
healthCheckerStatus
);
} else {
healthCheckerStatus = healthCheckers.statusWam1Wam2;
console.log(
"healthCheckerStatus value from tenant2 : ",
healthCheckerStatus
);
}
i++;
if (healthCheckerStatus !== true && i > 36) {
clearInterval(interval);
console.log("VMs are UNHEALTHY in the end of interval or before the end");
this.isProcessing = false;
throw new ApiError(
500,
"VMs are UNHEALTHY in the end of interval or before the end"
);
} else if (healthCheckerStatus == true) {
clearInterval(interval);
console.log(
`VMs ${labelData} are HEALTHY and successfully started!`
);
resultInterval.health = "healthy";
resultInterval.message = "VMs are successfully started!";
console.log("resultInterval into interval===>", resultInterval);
return resultInterval
}
}, 10000);
console.log("Script interval is running to check health");
return interval
}
export { checkHealthInterval };
getStartVms = async (req, res, next) => {
console.log("You asked to start VMs: starting VMs");
try {
this.isProcessing = true;
const responseStart = await this.instanceService.startInstances();
// use interval to checkHealth of Vms with import function checkHealth()
const resultInterval = await checkHealthInterval(responseStart?.labelData);
res.status(200).json({
health: "healthy",
message: "VMs are successfully started!",
});
this.isProcessing = false;
io.emit("processChanged", this.isProcessing);
} catch (err) {
this.isProcessing = false;
io.emit("processChanged", this.isProcessing);
next(err);
}
};

Node.js multiple requests at same time and one result returning

Multiple users can call requests at the same time, so I want to do if one user calls to request and calculation of results is being started then when another user calls the same request results calculation is not being started, but wait for results which were asked for the first user. In other words, the calculation of the result should be started only if it is not 'locked' by another user, if it is - then waits for the result.
Edited
Code:
Results:
To guarantee that the result is not calculated multiple times for concurrent requests, you need to implement some kind of locking mechanism (as you expected).
Here's a very basic example of what your code could look like, that simply pushes requests to a queue if the mutex is currently locked. If not the result gets calculated and for all pending requests a response with the calculated value will be sent:
const express = require('express');
const app = express();
class Mutex {
constructor() {
this.queue = [];
this.locked = false;
}
lock() {
return new Promise((resolve, reject) => {
if (this.locked) {
this.queue.push([resolve, reject]);
} else {
this.locked = true;
resolve();
}
});
}
release(value) {
if (this.queue.length > 0) {
const [resolve, reject] = this.queue.shift();
resolve(value);
} else {
this.locked = false;
}
}
}
const getResultsMutex = new Mutex();
function getResults() {
return new Promise((resolve => {
setTimeout(() => resolve(Math.random()), 5000);
}))
}
function sendResponse(result, req, res) {
res.send("The result is " + result);
}
app.get('/getresults', async (req, res) => {
let result = await getResultsMutex.lock();
if (!result) {
result = await getResults();
}
sendResponse(result, req, res);
getResultsMutex.release(result);
});
app.listen(4000, function () {
console.log("Server is running at port 4000");
});
Like this?
/** #type {[express.Request, express.Response][]} */
let requestQueue = [];
/**
* #param {express.Request} req
* #param {express.Response} res
*/
async function processRequest( req, res ) {
var result = await GetResults();
res.end(result);
if (requestQueue.length !== 0) processRequest.apply(requestQueue.shift());
}
app.get("/getresults", (req, res) => {
if (requestQueue.length !== 0) return requestQueue.push([req, res]);
processRequest(req, res);
});
EDIT: If they should all receive the same result, then this code:
/** #type {Promise} */
let requestPromise = null;
app.get("/getresults", (req, res) => {
if (requestPromise === null) requestPromise = GetResults().finally(() => requestPromise = null); // returns a promise
res.send(await requestPromise);
});

Handling promises inside the forEach loop

I am trying to run a series of tasks. Each task is dynamic, and could have different rules to follow. This will be executed on AWS-Lambda.
I have an array of JSON. It has a body with task name in it, and it also has attributes.
I need to dynamically load a javascript file with the name inside the body.
I need to wait until all is finished inside that task. Or it failed (regardless where). If the fail happens, I will need to write that data inside the current record inside the forEach loop.
I have old issue, where my forEach is finished first without waiting for the task to complete.
This is the forEach loop:
const jobLoader = require('./Helpers/jobLoader');
event.Records.forEach(record => {
const { body: jobName } = record;
const { messageAttributes } = record;
const job = jobLoader.loadJob(jobName);
job.runJob(messageAttributes).then(res => {
console.log('Show results');
return; // resume another record from forEach
}).catch(err => {
record.failed = true;
record.failureMessage = err.message;
console.log('I errored');
});
console.log('All Done');
});
The problem is that message All Done is printed, and then the message show results is printed. I get results from the database once it comes for execution.
This is the file that loads a task:
exports.loadJob = (jobName) => {
const job = require(`../Tasks/${jobName}`);
return job;
};
This is the file that contains actual task:
const mySqlConnector = require('../Storage/mySql');
exports.runJob = async (params) => {
let payload = {};
let dataToSend = await getUserName(params.userId.stringValue);
payload.dataToSend = dataToSend;
let moreDataToSend = await getEvenMoreData(params.userId.stringValue);
payload.moreDataToSend = moreDataToSend;
return await sendData(payload);
};
const getUserName = async (userId) => {
const query = 'SELECT * FROM user_data';
return await mySqlConnector.handler(query);
};
const getEvenMoreData = async (userId) => {
const query = 'SELECT * FROM user_data';
return await mySqlConnector.handler(query);
};
const sendData = (payload) => {
//this should be Axios sending data
};
And this is the mySql connector itself:
const mysql = require('promise-mysql');
exports.handler = async (query) => {
return mysql.createConnection({
host : '127.0.0.1',
user : 'root',
password : '',
database: 'crm'
}).then(conn =>{
let result = conn.query(query);
conn.end();
return result;
}).then(rows => {
//console.log("These are rows:" + rows);
return rows;
}).catch(error => {
return error;
});
};
The task file can have any number of things it needs to complete, which will be different when I start adding tasks.
I need that job.runJob completes, or that it catches an error, from whatever location it originated, so I can continue with the forEach.
I have tried using map and what not, but the end result is always the same.
What am I doing wrong?
You can use Promise.all method :
const promises = event.Records.map(record => {
const { body: jobName } = record;
const { messageAttributes } = record;
const job = jobLoader.loadJob(jobName);
return job.runJob(messageAttributes).then(res => {
console.log('Show results', res);
}).catch(err => {
record.failed = true;
record.failureMessage = err.message;
console.log('I errored');
throw new Error('Your error !');
});
});
try {
const results = await Promise.all(promises);
console.log('All done');
} catch (e) {
console.log('Something has an error', e);
}
don't forget to make your function async !
I managed to solve it, and still keep details about the execution:
Something like this:
for (let prop in event.Records){
const { body: jobName } = event.Records[prop];
const { messageAttributes } = event.Records[prop];
const job = jobLoader.loadJob(jobName);
await job.runJob(messageAttributes).then(res => {
console.log('Show results', res);
}).catch(err => {
event.Records[prop].failed = true;
event.Records[prop].failed = err.message;
console.log('I errored');
});
}

Polling a URL until certain value is set in JSON response : Mocha, Integration testing

I am working on automating an End to end scenario using Mocha.
I have a url endpoint which is to be polled until a certain value is obtained in the resulting response. Is there any way to do it ?
Example with request and callback approach:
const request = require('request');
describe('example', () => {
it('polling', function (done) {
this.timeout(5000);
let attemptsLeft = 10;
const expectedValue = '42';
const delayBetweenRequest = 100;
function check() {
request('http://www.google.com', (error, response, body) => {
if (body === expectedValue) return done();
attemptsLeft -= 1;
if (!attemptsLeft) return done(new Error('All attempts used'));
setTimeout(check, delayBetweenRequest);
});
}
check();
});
});
Example with got and async/await approach:
const utils = require('util');
const got = require('got');
const wait = utils.promisify(setTimeout);
describe('example', () => {
it('polling', async function (done) {
this.timeout(5000);
const expectedValue = '42';
const delayBetweenRequest = 100;
for (let attemptsLeft = 10; attemptsLeft; attemptsLeft -= 1) {
const resp = await got.get('http://www.google.com');
if (resp.body === expectedValue) return done();
await wait(delayBetweenRequest);
}
done(new Error('All attempts used'));
});
});
This is how I was able to do it with WebdriverIO and Mocha
describe("wait for value in content of page", () => {
it("should be able to wait to value in url", () => {
var max_seconds_to_wait = 10;
var seconds_counter = 0;
var should_continue = true;
while (should_continue) {
browser.url('http://your.url.com');
var response = JSON.parse(browser.getText("body"));
console.log(response)
if (response == 'something') {
should_continue = false;
}
browser.pause(1000);
seconds_counter++;
if (seconds_counter > max_seconds_to_wait) {
throw 'Waiting for json from url timeout error';
}
}
});
});

Nodejs check file exists, if not, wait till it exist

I'm generating files automatically, and I have another script which will check if a given file is already generated, so how could I implement such a function:
function checkExistsWithTimeout(path, timeout)
which will check if a path exists, if not, wait for it, util timeout.
Assuming you're planning on using Promises since you did not supply a callback in your method signature, you could check if the file exists and watch the directory at the same time, then resolve if the file exists, or the file is created before the timeout occurs.
function checkExistsWithTimeout(filePath, timeout) {
return new Promise(function (resolve, reject) {
var timer = setTimeout(function () {
watcher.close();
reject(new Error('File did not exists and was not created during the timeout.'));
}, timeout);
fs.access(filePath, fs.constants.R_OK, function (err) {
if (!err) {
clearTimeout(timer);
watcher.close();
resolve();
}
});
var dir = path.dirname(filePath);
var basename = path.basename(filePath);
var watcher = fs.watch(dir, function (eventType, filename) {
if (eventType === 'rename' && filename === basename) {
clearTimeout(timer);
watcher.close();
resolve();
}
});
});
}
fs.watch() API is what you need.
Be sure to read all the caveats mentioned there before you use it.
import fs from 'node:fs'; //es6
//or
const fs = require('fs'); //commonjs
/**
*
* #param {String} filePath
* #param {Number} timeout
* #returns {Promise<Boolean>}
*/
const holdBeforeFileExists = async (filePath, timeout) => {
timeout = timeout < 1000 ? 1000 : timeout
try {
var nom = 0
return new Promise(resolve => {
var inter = setInterval(() => {
nom = nom + 100
if (nom >= timeout) {
clearInterval(inter)
//maybe exists, but my time is up!
resolve(false)
}
if (fs.existsSync(filePath) && fs.lstatSync(filePath).isFile()) {
clearInterval(inter)
//clear timer, even though there's still plenty of time left
resolve(true)
}
}, 100)
})
} catch (error) {
return false
}
}
(async()=>{
const maxTimeToCheck = 3000; //3 second
const fileCreated = '/path/filename.ext';
const isFile = await holdBeforeFileExists(fileCreated, maxTimeToCheck);
//Result boolean true | false
})();
It's work goodssssssssssss................!!!
Try it before giving bad comments.
Enjoy your Kopi mana kopi obat kagak ngantuk???
express js:
router.get('some_url', async(req, res)=>{
const fileCreated = someFunctionCreateFileWithResultStringPathName();
const maxTimeToCheck = 3000; //3 second
const isFile = await holdBeforeFileExists(fileCreated, maxTimeToCheck);
if(isFile){
res.sendFile(fileCreated)
} else {
res.send('Failed to generate file, because use a bad function to generate file. or too long to create a file');
}
})
Here is the solution:
// Wait for file to exist, checks every 2 seconds by default
function getFile(path, timeout=2000) {
const intervalObj = setInterval(function() {
const file = path;
const fileExists = fs.existsSync(file);
console.log('Checking for: ', file);
console.log('Exists: ', fileExists);
if (fileExists) {
clearInterval(intervalObj);
}
}, timeout);
};
You could implement it like this if you have node 6 or higher.
const fs = require('fs')
function checkExistsWithTimeout(path, timeout) {
return new Promise((resolve, reject) => {
const timeoutTimerId = setTimeout(handleTimeout, timeout)
const interval = timeout / 6
let intervalTimerId
function handleTimeout() {
clearTimeout(timerId)
const error = new Error('path check timed out')
error.name = 'PATH_CHECK_TIMED_OUT'
reject(error)
}
function handleInterval() {
fs.access(path, (err) => {
if(err) {
intervalTimerId = setTimeout(handleInterval, interval)
} else {
clearTimeout(timeoutTimerId)
resolve(path)
}
})
}
intervalTimerId = setTimeout(handleInterval, interval)
})
}
Here another version that works for me :
async function checkFileExist(path, timeout = 2000)
{
let totalTime = 0;
let checkTime = timeout / 10;
return await new Promise((resolve, reject) => {
const timer = setInterval(function() {
totalTime += checkTime;
let fileExists = fs.existsSync(path);
if (fileExists || totalTime >= timeout) {
clearInterval(timer);
resolve(fileExists);
}
}, checkTime);
});
}
You can simply use it :
await checkFileExist("c:/tmp/myfile.png");
function verifyFileDownload(extension) {
browser.sleep(150000); //waiting for file to download
const fs = require('fs');
let os = require('os');
var flag = true;
console.log(os.userInfo());
fs.readdir('/Users/' + require("os").userInfo().username + '/Downloads/', (error, file) => {
if (error) {
throw error;
}
console.log('File name' + file);
for (var i = 0; i < file.length; i++) {
const fileParts = file[i].split('.');
const ext = fileParts[fileParts.length - 1];
if (ext === extension) {
flag = false;
}
}
if (!flag) {
return;
}
throw error;
});
};
This is very much a hack, but works for quick stuff.
function wait (ms) {
var now = Date.now();
var later = now + ms;
while (Date.now() < later) {
// wait
}
}

Resources