Stopping an async function without waiting for it to finish - node.js

In my system there is an updater function running every few seconds, this function looks something like this:
function updater() {
this.updater = setTimeout(async function() {
let data = await updatingFromInternet(url);
updater();
}, 5000);
}
I call the updater function once and it should update and run itself again 5 seconds after it last updated.
Since the updatingFromInternet function is an async function I can only wait for it to finish and then decide whether or not to keep updating but not stop it at the point it is currently waiting (fetching internet data).
Is there a simple way to stop the update immediately without the need to wait for the async function to return?

Yes as long as you have a reference to the timer handle:
clearTimeout(this.updater)

Instead of updater being a function, I would make it an object. Even with a cancellation featrue, if you called the updater function before the previous one was cancelled, this.updater would be overwritten and you couldn't cancel that timer, it would continue forever.
The below example uses an IIFE to encapsulate its variables and returns an object with a start and stop method.
const elOutput = document.getElementById('output'),
elStatus = document.getElementById('status');
const url = '';
// IIFE that returns the updater object
const updater = (function () {
let timer = null;
let update = function () {
timer = setTimeout(async () => {
elStatus.textContent = 'Waiting for data...';
let data = await updatingFromInternet(url);
// if timer has been set to null, updates have been
// cancelled
if (timer !== null) {
// do something with data here
elOutput.textContent = data;
elStatus.textContent = 'Updating...';
update();
}
}, 5000);
};
let start = () => {
// if the timer is not already running...
if (timer === null) {
elStatus.textContent = 'Starting update timer...';
update();
}
};
let stop = () => {
clearTimeout(timer);
timer = null;
elStatus.textContent = 'Updates Canceled';
};
// return our updater object with its start and stop methods
return {
start,
stop
};
}());
document.getElementById('update').addEventListener('click', updater.start, false);
document.getElementById('cancel').addEventListener('click', updater.stop, false);
// mocking a networks response
const updatingFromInternet = (function () {
let counter = 0;
return function (url) {
return new Promise((res, rej) => {
setTimeout(res.bind(null, counter++), 1000);
});
};
}());
<button type="button" id="update">Update</button>
<button type="button" id="cancel">Cancel updates</button>
<div id="output"></div>
<div id="status"></div>
Even better, since you are using Node, this is the sort of thing that would be ideal to put in a module.
// updater.js
let updatingFromInternet = require('./updatingFromInternet.js');
let timer = null;
let callback = null;
let update = function () {
timer = setTimeout(async () => {
let data = await updatingFromInternet(url);
// if timer has been set to null, updates have been
// cancelled
if (timer !== null) {
callback(data);
update();
}
}, 5000);
};
let start = (userCallback) => {
// if the timer is not already running
// and we received a callback function
if (timer === null && typeof userCallback === 'function') {
callback = userCallback;
update();
}
};
let stop = () => {
clearTimeout(timer);
timer = null;
};
module.exports = {
start,
stop
};
Then, to use it, require it in your main file:
let updater = require('./updater.js');
updater(function (data) {
console.log(data);
}

Related

Call a async function every minute for 5 minutes in nodejs

I am trying to call a async function every minute for 5 minutes before exiting the main function. Below is the print_data() function which I am calling in main() function.
var print_data = async () => {
console.log("Hello")
}
async function main() {
process.stderr.write("--Start--")
var data = await print_data()
console.log(data)
}
main()
Very new to writing async function. What is the best way to call print_data function every minute for 5 minutes and print the output each minute? I tried something using setInterval and was not able to execute the function completely.
Any help would be good. Thank you in advance.
This is one way to do it using setInterval and clearInterval. Read more about it here: https://nodejs.org/api/timers.html#timers_clearinterval_timeout
Using IIFE to prevent polluting of the global scope.
(function (){
let counter = 0; //counter to keep track of number of times the setInterval Cb is called
let data; // to store reference of Timeout object as returned by setInterval, this is used in clearInterval
const print_data = async () => {
console.log("Hello")
counter++;
if (counter == '5') {
clearInterval(data);
}
}
async function main() {
process.stderr.write("--Start--")
data = setInterval(print_data, 1000*60); //60 seconds
}
main();
})();
Please check if the below code can be a solution.
var print_data = async () => {
console.log("Hello")
return "Hello";
}
var call_print_data = () => new Promise((resolve, reject) => {
var count = 0;
var interval = setInterval(async () => {
var res = await print_data();
count += 1;
if (count === 5) { // if it has been run 5 times, we resolve the promise
clearInterval(interval);
resolve(res); // result of promise
}
}, 1000 * 60); // 1 min interval
});
async function main() {
process.stderr.write("--Start--")
var data = await call_print_data(); // The main function will wait 5 minutes here
console.log(data)
}
main()

Async.queue crashes after processing 3 initial elements of the queue with concurrency = 3

Async.queue() intially runs as expected but crashes after processing the first N elements (N = 3).
When adding callback() after running getAddress(), concurrency is totally ignored. Subsequently getAddress() runs for all tasks passed to the queue via the stream.
The problem arose when attempting to build upon this tutorial.
Trying to determine the root cause and a solution. Seems possible that this is related to promise chaining?
Have attempted to refactor async.queue() following the async docs, but appears that the syntax is out of date and can't find a working example with chained promises.
const { csvFormat } = require('d3-dsv');
const Nightmare = require('nightmare');
const { readFileSync, writeFileSync } = require('fs');
const numbers = readFileSync('./tesco-title-numbers.csv',
{encoding: 'utf8'}).trim().split('\n');
const START = 'https://eservices.landregistry.gov.uk/wps/portal/Property_Search';
var async = require("async")
console.log(numbers)
// create a read stream
var ArrayStream = require('arraystream')
var stream = ArrayStream.create(numbers)
// set concurrency
N = 3
var q = async.queue(function (task, callback) {
let data = getAddress(task)
// , function(){
// callback();
},
// },
N);
q.drain = function() {
stream.resume()
console.log('all items have been processed');
resolve()
}
// or await the end
// await q.drain()
q.saturated = function() {
stream.pause();
}
// assign an error callback
q.error = function(err, task) {
console.error('task experienced an error');
}
stream.on("data", function(data) {
// console.log(data);
q.push(data)
})
var getAddress = async id => {console.log(`Now checking ${id}`);
const nightmare = new Nightmare({ show: true });
// Go to initial start page, navigate to Detail search
try {
await nightmare
.goto(START)
.wait('.bodylinkcopy:first-child')
.click('.bodylinkcopy:first-child');
} catch(e) {
console.error(e);
}
// Type the title number into the appropriate box; click submit
try {
let SOMEGLOBALVAR;
await nightmare
// does some work
} catch(e) {
console.error(e);
return undefined;
}
};
Determined the cause of the problem. The callback along with getAddressed needs to be returned.
let dataArray = []
N = 4
var q = async.queue(async function (task, callback) {
return getAddress(task).then((response)=>{
console.log(response);
dataArray.push(response);
callback()})
} ,
N);

How to abort async function when 'stop' event has been emitted

I trying to make a puppeteer.js bot to be able to pause and resume its work.
In general, i have a class with a dozen of async methods, event emitter and a property called 'state' with setter to change it. When I have event 'stop', I want some async functions to be aborted. How can I achieve this?
I thought i need to observe when this.state becomes 'stop', and run return; but hadn't found any solution.
Then I decided to try to set a handler on an event which changes state to 'stop', but I cannot abort async functions from the handler on the stop event.
constructor() {
this.state = 'none';
this.emiter = new events.EventEmitter();
this.setHandler('stop', () => this.stop());
this.setHandler('resume', () => this.resume());
this.setHandler('onLoginPage', () => this.passAuth());
// ...
// And dozen of other states with its handlers
}
stop= () => this.setState('stoped', true);
resume = () => this.setState(this.getPreviousState());
getPreviousState = () => ...
// Just an example of a state handler. It has async calls as well
// I want to abort this function when event 'stop' is emitted
#errorCatcher()
async passAuth() {
const { credentials } = Setup.Instance;
await this.page.waitForSelector(LOGIN);
await typeToInput(this.page, EMAIL_INPUT, credentials.login);
await typeToInput(this.page, PWD_INPUT, credentials.pass);
await Promise.all([
await this.page.click(LOGIN),
this.page.waitForNavigation({ timeout: 600000 }),
]);
await this.page.waitFor(500);
await DomMutations.setDomMutationObserver(this.page, this.socketEmitter);
// ...
// And dozen of handlers on corresponding state
setState(nextState, resume) {
// Avoiding to change state if we on pause.
// But resume() can force setstate with argument resume = true;
if (this.state === 'stoped' && !resume) return false;
console.log(`\nEmmited FSM#${nextState}`);
this.emiter.emit(`FSM#${nextState}`);
}
setHandler(state, handler) {
this.emiter.on(`FSM#${state}`, async () => {
this.state = state;
console.log(`State has been changed: ${this.getPreviousState()} ==> ${this.state}. Runnig handler.\n`);
//
// On the next line, we run a corresponding handler func,
// like passAuth() for state 'onLoginPage'. It has has to be aborted
// if emiter gets 'FSM#stoped' event.
//
await handler();
});
}
}```
I expect the async functions to be aborted when event emitter emits 'stop';
It is impossible to do it natively.
Alternatively, there are two other way to do it.
check your state after any call of await, for example:
class Stated {
async run() {
await foo()
if(this.stopped) return
await bar()
if(this.stopped) return
await done()
}
}
const s = new Stated()
s.run()
use generator with custom wrapper rather than async/await.
// the wrapper
function co(gen, isStopped = () => false) {
return new Promise((resolve, reject) => {
if (!gen || typeof gen.next !== 'function') return resolve(gen)
onFulfilled()
function onFulfilled(res) {
let ret
try {
ret = gen.next(res)
} catch (e) {
return reject(e)
}
next(ret)
}
function onRejected(err) {
let ret
try {
ret = gen.throw(err)
} catch (e) {
return reject(e)
}
next(ret)
}
function next(ret) {
if (ret.done || isStopped()) return resolve(ret.value)
Promise.resolve(ret.value).then(onFulfilled, onRejected)
}
});
}
// the following is your code:
class Stated {
* run() {
yield foo()
yield bar()
yield done()
}
}
const s = new Stated()
co(s.run(), () => s.stopped)

gpio.read null false while trying to read input

I have been trying to read values from an ultrasonic sensor.
the code i am using
//callbacks; got from a previous post
var gpio_read = function (channel) {
new Promise(resolve => {
gpio.read(channel, function (error, result) {
console.log('gpio.read', error, result);
resolve(result);
});
});
}
//
var off = function () {
gpio.write(trig, 0);
}
tank.getDistance = function () {
var start, stop;
gpio.write(trig, 0);
gpio.write(trig, 1);
setTimeout(off, 10);
while (gpio_read(echo) === 0) {
start = Date.now();
console.log("nosig");
}
while (gpio_read(echo) === 1) {
stop = Date.now();
console.log("sig");
}
console.log(stop - start);
};
// pin setup
tank.initPins = function () {
async.parallel([
gpio.setup(p7, gpio.DIR_OUT),
gpio.setup(p11, gpio.DIR_OUT),
gpio.setup(p13, gpio.DIR_OUT),
gpio.setup(p15, gpio.DIR_OUT),
gpio.setup(echo, gpio.DIR_IN),
gpio.setup(trig, gpio.DIR_OUT)
]);
};
i wrote similar python code and i get values back but here i get
gpio.read null false
gpio.read null true
I dont know why ?
I though it was due to busy pins so i tried resetting them before use and all. Any ideas?
You don't need to wrap the gpio.read in a Promise.
var gpio_read = function (channel) {
gpio.read(channel, function (error, result) {
console.log('gpio.read', error, result);
return result;
});
}
The way you had it written (new Promise(...)) would just create a Promise but never actually return it to the calling function. For that, you would need return new Promise(...) and to change the calling code to wait for the promise (eg gpio_read.then(fn)).
In the case of gpio.read it will already return a value once it is read from the board so you don't need to wrap it in a promise.

How to run a callback when all running in parallel function done their job?

I got stack with the question:
"How to run a callback after all async functions done their job"
Here an example:
function doTasks(**callback**) {
doTask1(function() {
...
});
doTask2(function() {
...
});
}
I do not want to run a task after another. The idea to run them in parallel, but I need that callback right after all done. Does nodeJs have build-in feature for it?
For now I'm using a combination of an EventEmitter and counter. Every time when a task is finished it runs an event. Because I know how many tasks have ran. I can count it and emit callback. But must be more flexible way. Here what I use now.
var EventEmitter = require("events").EventEmitter;
var MakeItHappen = module.exports = function (runAfterTimes, callback) {
this._aTimes = runAfterTimes || 1;
this._cTimes = 0;
this._eventEmmiter = new EventEmitter();
this._eventEmmiter.addListener("try", callback);
}
MakeItHappen.prototype.try = function () {
this._cTimes += 1;
if (this._aTimes === this._cTimes) {
this._cTimes = 0;
this._eventEmmiter.emit("try", arguments);
}
}
Is there another way to do it?
You can use the async library, https://github.com/caolan/async
async.parallel([
function(){ ... },
function(){ ... }
], callback);
Use a counter:
var a = function (cb){
//Asynchronous stuff
cb ();
};
var b = function (cb){
//Asynchronous stuff
cb ();
};
var remaining = 2;
var finish = function (){
if (!--remaining){
//a and b finished...
}
};
a (finish);
b (finish);

Resources