Node.js async.whilst() is not executing at all - javascript

I'm just trying to use async.whilst() as seen here.
Here is my simple code, taken from their docs:
var async = require('async');
console.log('start');
async.whilst(
function () { return true; },
function (callback) {
console.log('iteration');
callback();
},
function (err) {
console.log('end');
},
);
When I run this, the loop doesn't run. Only start is printed out.

Because you are returning true, that why the callback for function 1 wasn't called. So you only see the 'start'.
You can find some information below:
const async = require('async');
let count = 0;
const compareVariable = 10;
console.log('start');
async.whilst(
function functionName1(callbackFunction) {
// perform before each execution of iterFunctionInside, you need a condition(or other related condition) in 2nd params.
callbackFunction(null, count < compareVariable)
},
// this func is called each time when functionName1 invoked
function iterFunctionInside(callback) {
// increase counter to compare with compareVariable
count++;
console.log('iteration', count);
// if you want to show like tick time, you can set timeout here or comment out if you dont want
setTimeout(() => {
callback(null, count);
}, 1000)
},
function (err, n) {
console.log('end');
},
);

Related

Loop iteration with time interval before passing value to included function

I'm trying to figure out how to set time out for function inside the loop iteration in Ionic TypeScript application.
setInterval makes equal time interval with calling the function in endless repetition:
setInterval(() => {
this.myFunc1(val);
}, 800);
setTimeout gives required result if listed sequentially:
setTimeout(() => {
this.myFunc1(val);
}, 800);
setTimeout(() => {
this.myFunc1(val);
}, 1200);
but how to loop with time interval trough the updated list and wait while pass second value val to the function, or call myFunc1 when it will be finished in previous iteration:
async myFunc2() {
for (let val of this.myValueList) {
/// wait for 5 sec or wait for finishing process, then pass value calling function:
this.myFunc1(val);
}
}
setInterval is the correct choice here. What's missing is that you need to clear the interval. setInterval returns an id that you can pass to clearInterval which stops the iteration.
Here I'm passing data to console.log, waiting a second, then repeating till done.
const myValueList = [5,6,7,8,9,10];
let i = 0;
const id = setInterval(() => {
console.log(myValueList[i++]);
if (i === myValueList.length) {
clearInterval(id);
console.log("done!");
}
}, 1000);
Simplest solution for waiting approximately 5 seconds:
const wait = t => new Promise(r => setTimeout(r, t));
and then in your code you can just do:
async myFunc2() {
for (let val of this.myValueList) {
await wait(5000);
this.myFunc1(val);
}
}
If myFunc1 is async, and you just want to wait for it to finish executing before continuing the loop, then you would just do:
async myFunc2() {
for (let val of this.myValueList) {
await this.myFunc1(val);
}
}

Puppeteer - Is there a way to pass a function as a parameter to a function that loaded via exposeFunction?

I have a node js project that uses puppeteer, and I would like to use the library async-wait-until within the page context.
This library receives a function as a parameter, tries running it until it returns a truthy value or timeout passes.
The issue is that I get the following error:
Error: predicate is not a function
having predicate the name of the variable that holds the function.
Some code:
This is where I launch the pages - where I expose the function:
const waitUntil = require('async-wait-until');
// const waitUntil = require('./node_modules/async-wait-until/src/waitUntil.js');
await page.exposeFunction('waitUntil', waitUntil);
Here I am trying to run the function
try {
await waitUntil(() => {
//Code should come here
return false;
}, 25000, 1000);
console.log('Element Is Ready');
} catch (e) {
console.log('CATCH');
console.log(e.toString());
}
And here is the internal js file of the exported function waitUntil:
var DEFAULT_INTERVAL = 50;
var DEFAULT_TIMEOUT = 5000;
/**
* Waits for predicate to be truthy and resolves a Promise
*
* #param predicate Function Predicate that checks the condition
* #param timeout Number Maximum wait interval, 5000ms by default
* #param interval Number Wait interval, 50ms by default
* #return Promise Promise to return a callback result
*/
module.exports = function waitUntil(
predicate,
timeout,
interval
) {
var timerInterval = interval || DEFAULT_INTERVAL;
var timerTimeout = timeout || DEFAULT_TIMEOUT;
return new Promise(function promiseCallback(resolve, reject) {
var timer;
var timeoutTimer;
var clearTimers;
var doStep;
clearTimers = function clearWaitTimers() {
clearTimeout(timeoutTimer);
clearInterval(timer);
};
doStep = function doTimerStep() {
var result;
try {
console.log('running predicate function');
console.log(predicate);
console.log(timeout);
console.log(interval);
result = predicate();
console.log('ran predicate function');
if (result) {
clearTimers();
resolve(result);
} else {
timer = setTimeout(doStep, timerInterval);
}
} catch (e) {
clearTimers();
reject(e);
}
};
timer = setTimeout(doStep, timerInterval);
timeoutTimer = setTimeout(function onTimeout() {
clearTimers();
reject(new Error('Timed out after waiting for ' + timerTimeout + 'ms'));
}, timerTimeout);
});
};
When running the code, the prints I get are:
null
25000
1000
Meaning predicate (the function that is being sent) is being recognized as null.
Tried all the different ways (saving it as a function/function expression/anonymous function, with without async), nothing works.
When sending a string instead of the function, the prints do print the string itself, and not null.
Is there something I am doing wrong?
Is there a way to bypass it?
Thanks
It seems, exposed functions, as well as evaluated functions, can only use serializable arguments, so functions cannot be transferred between Node.js and browser contexts as parameters. Maybe you can try a wrapper and a dictionary of functions, so you can transfer function names as strings:
const waitUntil = require('async-wait-until');
const functions = {
foo() {
//Code should come here
return false;
},
bar() {
//Code should come here
return false;
},
};
await page.exposeFunction('waitUntil', async (functionName, param1, param2) => {
waitUntil(functions[functionName], param1, param2);
});
await page.evaluate(() => {
await window.waitUntil('foo', 25000, 1000);
});

Javascript delay a loop till iframe is loaded in first iteration

I have a javascript loop that executes a couple of functions. The first function loads an iframe and the second function clicks an element on that iframe. I want the loop not to run the second function until the iframe is finished loading. But I am not sure how to achieve this.
So far I have done this but doesn't seem to do the job
Loop :
action.steps.forEach(step => {
window[step.functionName](step.functionParameter);
});
First function
function goToUrl(url) {
let iframeDocument = document.querySelector('iframe');
iframeDocument.src = url;
let iframeLoaded;
iframeDocument.onload = () => {
iframeLoaded = true
}
async function checkLoad() {
if (iframeLoaded) {
alert("page loaded");
return true;
} else {
await sleep(500);
checkLoad();
}
}
checkLoad();
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
second function
function clickOnElement(elementSelector) {
var element = iframeDocument.querySelector(elementSelector);
element.click();
}
The first function is an asynchronous operation, which can be modified to return a Promise that resolves when the frame is loaded. This also means you don't need to recursively checkLoad.
function goToUrl(url) {
const iframeDocument = document.querySelector('iframe');
iframeDocument.src = url;
return new Promise((resolve, reject) => {
// calls `resolve()` when loaded
iframeDocument.onload = resolve;
});
}
The second function needs to wait for the first function to be resolved.
To generalise this pattern, you can modify your loop as an asynchronous function,
which awaits for the result of a step if that step's function returns a Promise (e.g. your goToUrl function:
async function yourLoop() {
// each step could be synchronous or asynchronous
for (const step of actions.step) {
const result = window[step.functionName](step.functionParameter);
if (result instanceof Promise) {
// if step is asynchronous operation, wait for it to complete
await result;
}
}
}
/////// usage ////////
yourLoop().then(() => {
/* all steps completed */
}).catch(() => {
/* some step(s) failed */
});
Whenever a timer or event handler is needed, I usually prefer not to do a traditional loop nor mess with promises/awaits.
Instead what I would do is something similar to this. Basically waiting in onload to run the callback function then moving on to the next function in the loop.
let max = action.steps.length;
let cur = 0;
function loadIframe(url) {
let iframeDocument = document.querySelector('iframe');
iframeDocument.src = url;
let iframeLoaded;
iframeDocument.onload = () => {
let step = action.steps[cur];
window[step.functionName](step.functionParameter);
if(cur < max){
cur++;
loadIframe(step.url)
}
}
}
loadIframe(action.steps[cur].url);

JS: Get inner function arguments in asynchronous functions and execute callback

I try to write the function that returns all results of asynchronous functions and execute a callback that push into an array and log the result of every async function.
As a waiter that brings all dishes when they are all done.
I don't understand how to get the child arguments that should be returned as a result. The code of task and my not working solution is below:
The task:
var dishOne = function(child) {
setTimeout(function() {
child('soup');
}, 1000);
};
var dishTwo = function(child) {
setTimeout(function() {
child('dessert');
}, 1500);
};
waiter([dishOne, dishTwo], function(results) {
console.log(results); // console output = ['soup', 'dessert']
});
My not working solution:
function child(arg) {
this.arr.push(arg)
}
function waiter(funcArray, doneAll) {
var result = {
arr: []
};
let i = 0;
const x = child.bind(result)
funcArray.forEach(function(f) {
f(x)
i++;
if(i == 2) {
doneAll(result.arr)
}
});
}
Problem is this part:
funcArray.forEach(function(f) {
f(x)
i++;
if(i == 2) {
doneAll(result.arr)
}
});
which is a synchronous function so when you check if(i == 2), you basically check, that you have called all async functions, but they did not returned anything yet, so all you know is, that the functions have been called, but result.arr is not yet populated.
You must move the doneAll(result.arr) expression into child callback, then it will be called by async function as it returns result.
Simpliest solution I can think of is writing your child as
function child(arg) {
if (this.arr.push(arg) === this.allCount) this.doneAll(this.arr);
}
and in your waiter function enhance result object
var result = {
arr: []
, allCount: funcArray.length
, doneAll: doneAll
};
This shall work, but has one drawback -- position of results does not keep position of functions in funcArray, the position of results is sorted by duration of async function, simply the first resolved would take first result etc. If this is a problem, you must pass also index to your child function to store result at precious position in result array and then the check by arr.length would not work, because JS array returns length as the highest index + 1, so if your last funcArray would fulfill first, it'll fill last index and the length of result.arr will be equal to this.allCount, so for keeping order of result the same as funcArray, you will need to store number of returned results as another number, increase that number with every new result and compare that number to allCount.
Or decrease allCount like so
function child(idx, arg) {
this.arr[idx] = arg;
if (--this.allCount === 0) this.doneAll(this.arr);
}
And modify your waiter function
function waiter(funcArray, doneAll) {
const result = {
arr: []
, allCount: funcArray.length
, doneAll: doneAll
};
funcArray.forEach(function(f, i) {
f(child.bind(result, i));
});
}
Why not Promise?
function dishOne() {
return new Promise(function(resolve, reject) {
setTimeout(function() { resolve('soup') }, 1000)
})
}
function dishTwo() {
return new Promise(function(resolve, reject) {
setTimeout(function() { resolve('dessert') }, 1500)
})
}
Your waiter function:
function waiter(dishes, callback) {
return Promise.all(dishes).then(callback)
}
And you can use it like this
waiter([dishOne(), dishTwo()], function(results) {
// Invoked when all dishes are done
console.log(results) // ['soup', dessert']
})
Much easier to understand. Right?

Nested context.executeQueryAsync with Deferred

How can I use nested context.executeQueryAsync with Deferred? Below is my code and I will explain what exactly I am looking for:
Code
function getValues() {
var dfd = $.Deferred(function () {
context.executeQueryAsync(function () {
var navigationItem = [];
// First Loop
while (termEnumerator.moveNext()) {
// Push Parent Terms in navigationItem array
navigationItem.push({ "name": ""});
// Get Sub Terms
context.executeQueryAsync(function () {
// Second Loop
while (termsEnum.moveNext()) {
// Push Sub Terms in navigationItem array
navigationItem.push({ "name": ""});
}
}, function (sender, args) {
console.log(args.get_message());
});
}
dfd.resolve(navigationItem);
}, function (sender, args) {
console.log(args.get_message());
dfd.reject(args.get_message());
});
});
return dfd.promise();
}
Basically I am trying to fetch Taxonomy (Terms & it's sub terms) in SharePoint Online using above code structure. Initially I have created an array named navigationItem and iterating through all the terms.
During iteration, first of all, I am pushing terms into this array and along with this, I am also getting it's sub terms if any and pushing it into the same array.
I want that code doesn't execute further until second loop completes it's execution. So that I will have final array while returning it to another function.
I want that code doesn't execute further until second loop completes
it's execution. So that I will have final array while returning it to
another function.
In this case, you need to have a defer for each executeQueryAsync.
Then, you need a create an overall defer to wait all of the async methods finished.
Here is the sample code for your reference:
(function ($) {
function executeQueryAsync(succeededCallback, failedCallback)
{
var period = Math.random() * 10000;
setTimeout(function () {
succeededCallback();
}, period);
}
function forEachAsync(items, funcAsync, callback)
{
var count = 0;
var total = $.Deferred();
function increment()
{
count++;
if(count == items.length)
{
total.resolve();
}
}
for (var i = 0; i < items.length; i++)
{
(function exec(item) {
var deferred = $.Deferred(function (defer) {
funcAsync(function () {
callback();
defer.resolve();
});
});
deferred.done(function () {
increment();
});
})(items[i]);
}
return total.promise();
}
var promise = forEachAsync([1, 2, 3, 4, 5], executeQueryAsync, function () {
console.log('call back executing + ' + Date.now());
});
promise.done(function () {
console.log("promise done");
});
})(jQuery);

Categories

Resources