Make asynchronous call inside loop until a value is found - javascript

I'm trying to do several calls inside a loop to an asynchronous API until the value that I want (true in this case) is returned. The issue is that I don't want to keep looping after that value is found and I don't want to execute the lines after the loop without the value returned from it...but so far I can't get that working. I think I'm doing something wrong here, but "I can't get the whole picture".
function isSelected(element, callback) {
// This is a little bit confusing for me...can I just get the value from
// getDataAsync without using isSelected function?
Office.select(element).getDataAsync(function (asyncResult) {
var result = true;
// some logic here that may change 'result'
callback(result);
});
}
function delete(elements) {
var index = -1;
for (var i = 0, (i < elements.length) && (index < 0); i++) {
isSelected(elements[i], function(result) {
if (result) { index = i; }; // ...and I don't want to keep "looping"
});
}
// I want to execute these lines after the for-loop is done
// ...do something with the "correct" index value
}

Have you tried Kriskowal's Q? There's a nice function called Q#allSettled:
Q.allSettled(promises)
.then(function (results) {
results.forEach(function (result) {
if (result.state === "fulfilled") {
var value = result.value;
} else {
var reason = result.reason;
}
});
});
So basically this is how it would work in your case:
var promises = [];
for(/* conditions */) {
promises.push(/* async call which returns a promise */);
}
Q.allSettled(promises).then(function(results) {
results.forEach(function (result) {
var value;
if (result.state === "fulfilled") {
value = result.value;
// do something with "value"
}
});
});
allSettled just makes sure the then will be executed regardless of whether or not the promise was successful or not, and you can check the value of the object you retrieve from your async call.

I am recommending three ways to doing it.
Using just JavaScript.
Using Async library.
Using underscore library.
Here you can see the javascript implementation:
You can do something like that:
You need to track home many times, you call the function, and how many times the callback happened
function delete(elements) {
var index = -1;
var stack=0;
for (var i = 0, (i < elements.length) && (index < 0); i++) {
stack++ // Go up on each loop
isSelected(elements[i], function() {
stack--; //Go down each callback
index = i;
if(stack==0) afterAllFinish() //When it return to 0 mean all callback have finished
});
}
function afterAllFinish(){
// I want to execute these lines after the for-loop is done
// ...do something with the "correct" index value
}
}
Using other libraries:
Please take a look at http://underscorejs.org/#after for the underscore way to solve it.
Please take a look at https://github.com/caolan/async#parallel to see the async way to solve it.

Related

My timeout closure still processes only the last value of the loop. Javascript

I have a loop that calls executepostaction with an object. The problem is that executepostaction only execute the last value of the loop. I've tried many closures and here I am trying to fix using setTimeout but still no luck. What seems to be the problem?
my timeout function:
function MakeTimeout(fn, data, timeout) {
setTimeout(function () { fn.call(null, data); }, timeout);
}
This is the loop from an event function:
for (var ctr = 0; ctr < Selectrows.length; ctr++) {
var action= selectedAction;
action["trackId"] = Selectrows[ctr].innerText.replace(/(^\d+)(.+$)/i, '$1');
MakeTimeout(function (passaction) {
researchService.postExecuteAction(passaction)
.then(function (result) {
}, function error(result) {
$scope.error = result;
});
}, action, ctr * 1000);
}
Please help. Thank you
I see a few problems there, not sure exactly what you are trying to do but seems that you want to call a promise for each value of an array.
Guessing you are able to use ES6 and that each call are async, I would do something like this:
Selectrows.forEach(row => {
const action = {
...selectedAction,
trackId: row.innerText.replace(/(^\d+)(.+$)/i, '$1')
};
researchService.postExecuteAction(action)
.then(function (result) {
// Do something
}, function error(result) {
$scope.error = result;
});
});
First is using forEach instead of for if Selectrows is an array.
Clone the object selectedAction bc if you just assign it you are using the same object each time, bc objects and arrays are reference types.
Seems that you don't need a timeout, or do you? each promise will be executed in parallel and will response as soon as the promise return a response.
You can use spread syntax to create a copy of the object before modifying it.
var action = {...selectedAction};

Iterating over synchronous ajax calls with promises

I currently have some jQuery code that looks a bit like this:
for ( i = 0; i < limitVar; i++ ) {
doAjaxStuff(i);
}
function doAjaxStuff( i ) {
Here we make a SYNCHRONOUS ajax call, sending i.
}
The ajax call needs to be synchronous - one isn't fired until the last one is done.
As synchronous JS is deprecated, I want to move this code to use promises. How would I achieve this? I've been unable to find an example that close enough fits this situation.
You don't do synchronous ajax in the browser (well technically, you can in some circumstances, but it's a really bad idea to do so because it locks up the browser during the ajax call).
Instead, you redesign your loop so that it only carries out the next ajax call when the previous one is done which means you have to loop manually, you can't use a for loop. Since your code is pseudo-code (you don't show the real ajax operation), I'll use a jQuery ajax example, but you can substitute any ajax function you have as long as it either returns a promise or uses a callback to signal when its done.
The general idea is that you create a function for your ajax call and you use the completion callback from that to increment your index and then run the next iteration of your loop.
function runLoop(data) {
var i = 0;
function next() {
if (i < data.length) {
return $.ajax(data[i]).then(function(data) {
++i;
return next();
});
else {
// all done with loop
}
}
return next();
}
// call it like this
runLoop(someArray).then(function() {
// all done here
});
If you don't have an array of data, but just want a loop index:
function runLoop(limitVar) {
var i = 0;
function next() {
if (i < limitVar) {
return $.ajax(something_with_i_in_it).then(function(data) {
++i;
return next();
});
else {
// all done with loop
}
}
return next();
}
// call it like this
runLoop(theLimit).then(function() {
// all done here
});
If your limitVar is not large and there is no other logic involved in deciding whether to continue the loop, you can also use a little bit simpler pattern if you have an ajax function that returns a promise:
function runLoop(limitVar) {
var p = Promise.resolve();
for (var i = 0; i < limitVar; i++) {
p = p.then(function(prevResult) {
return someAjax(i);
});
}
return p;
}
// call it like this
runLoop(theLimit).then(function() {
// all done here
});
If you aren't using ajax functions that return a promise, then it's only a few lines of code to wrap your function with one that does and then you can more easily use these design patterns.
Process the array using some separate function. Each time you take off another element from array, then process it and when it's done call the function again. If there is no more item in list then the whole process is done.
var listOfRequests = ...;
new Promise( function( resolve, reject ) {
requestNext();
function requestNext() {
if ( !listOfRequests.length ) {
return resolve();
}
var next = listOfRequests.shift();
doAjaxStuff( next, reject, requestNext );
}
} )
doAjaxStuff( request, errCallback, doneCallback ) {
...
}
this is a pretty simple pattern:
var queue = Promise.resolve();
var nop = () => null;
for(let i=0; i<limitVar; ++i){
queue = queue.then(() => doAjaxStuff(i));
//or if you want to ignore Errors
//queue = queue.then(() => doAjaxStuff(i)).catch(nop);
}
queue.then(() => console.log("finished"));
Or if you use an Array as input:
var done = data.reduce(
(queue, value, index) => queue.then(() => doSomethingWith(value, index)),
Promise.resolve()
);
done.then(() => console.log("finished"));

NodeJS recursive call inside for loop, how to know when all calls are done?

I am writing a recursive function inside for loop like below:
var output = [];
function myFunc(myValue, callback) {
myAnotherFunc(myValue, function(result){
for (var i=0; i < result.myKey.length; i++){
if(result.myKey[i].name === 'something'){
myFunc(result.myKey[i].recurseValue, function(recursiveResult){
//some recursive stuff
output.push(recursiveResult.someValue)
});
}
}
});
}
And initiating the recursive function like below:
myFunc(initialValue, function(result){
//some stuff
});
Its working fine, but how do I know when my recursive flow ends so that I can do something else from the final output?
You can use Promises™! It's basically a way to defer a callback till after an Asynchronous flow is completed: Example:
// Instead of passing your normal callback, we'll tell the
// function to use resolve(results) to pass your results to the
// next code block so it can do something after all your recursions are completed
const someTask = new Promise(resolve => myFunc(initialValue, resolve))
someTask.then(result => {
/* Do Something with the results at the end of aformentioned callback hell :D */
})
PS. You also have to modify your original function signature to:
function myFunc(myValue, callback) {
myAnotherFunc(myValue, function(result){
const cbks = [] //Store the async resuls of all myFunc() executions
for (i=0; i < result.myKey.length; i++){
if(results[i] === 'something'){
cbks.push(new Promise(res => myFunc(result[i].recurseValue, res)))
}
}
//Run all async myFunc() and return the results in an array
Promise.all(cbks).then(callback)
});
}
function myFunc(resolve) {
var rec = function(myVal, cb) {
myOther(recurseValue, function(result) {
var hasName = result[myKey].filter(function(obj) {
return obj.name === 'something';
})[0];
if (hasName) {
rec(hasName[recurseValue], function(recResult) {
// other recursive stuff
});
} else {
resolve(?); // whatever the final value should be
}
});
};
return rec;
}
function recurseAsync(f, initial) {
return new Promise(function(resolve, reject) {
f(resolve)(initial);
});
}
Couple notes.
The recurseAsync function takes a function that takes a resolution callback and returns a recursive function that calls that callback when finished to resolve the promise. myFunc has been altered to fit that format.
I used array filtering rather than a for loop and shortened some names. Also if you are using a variable for object access use [] instead of .. To use the final value when all of this is finished you can call .then on the promise.
// whatever initial value 'foo' should be
var finished = recurseAsync(myFunc, foo);
finished.then(function(finalValue) {
// do something with the final result of all the recursion
});

Javascript - Global variable not being updated

I am trying to update a global variable within a callback function (see code below)
function FunctionOne() {
var result = "";
for (var i = 0; i < 10; i++) {
AjaxFunction(function (data) {
result += data;
});
}
alert(result);
}
function AjaxFunction(callback) {
$.ajax({
url: '/test',
type: 'GET',
success: function(data) {
callback(data);
}
});
}
When I alert result it always seems to be blank. Can you guys please tell me whats wrong?
Result is not global, it is only defined within the scope of FunctionOne(). However in your example you are alerting the value, before it has been assigned as the Ajax call appears to run asynchronously. Instead try alerting after you update your value.
function FunctionOne() {
var result = "";
for (var i = 0; i < 10; i++) {
AjaxFunction(function (data) {
result += data;
alert(result); // this should have the value
}
}
alert(result); // this should be empty
}
There are two parts to your question that need to be resolved - the first relates to the asynchronous nature of AJAX and how results are returned - for that see the canonical answer to the question that this was originally marked as a duplicate of.
The second part is how to make the final answer depend on the asynchronous completion of all ten AJAX requests:
function functionOne() {
// initiate ten requests, storing the promises in an array
var def = [];
for (var i = 0; i < 10; ++i) {
def[i] = AjaxFunction();
}
// wait for all the promises to complete, with the
// eventual result also being returned as a promise
return $.when.apply($, def).then(function() {
// take a copy of the arguments
var args = [].slice.call(arguments, 0);
// concatenate them all together
return args.reduce(function(prev, current) {
return prev + current[0]; // each arg is actually a 3-element array
}, "");
});
}
function AjaxFunction() {
return $.get('/test'); // NB: using promises - no callback required
};
// NB: functionOne() is now also async - you have to wait
// for the promise to be resolved before using the result
functionOne().then(function(result) {
alert(result);
)};
See http://jsfiddle.net/alnitak/5s28G/ for a working demo.

Check several values retrieved asynchronously from indexed DB

I need to retrieve several values from an IndexedDB, check if all of them fulfill some constraint (different for all of them) and if so call a function. To illustrate this better imagine that calls to IndexedDB were sychronous, we would have something like.
function myFunc(varNames, conditions) {
for(var i = 0; i < varNames.length; i++) {
if(!isGood(db.get(varNames[i]), conditions[i])) {
return;
}
}
doStuff();
}
Since IndexedDB is are asynchronous I do not konw how to do it. Using a callback in every get is not really feasible since the call to doStuff depends on all the gets. Accumulating the results in a global variable would not work either because myFunc is called more than one. I tried something like:
function myFunc(varNames, conditions) {
var valid = 0;
checker() {
valid++;
if(valid == varNames.length) {
doStuff();
}
}
for(var i = 0; i < varNames.length; i++) {
db.get(varNames[i], function(val) {
if(isGood(val, conditions[i]) {
checker();
}
});
}
}
But that does not seems to work either. Any ideas?
You can make the DB calls one at a time, and use the success callback to make the next DB call. Something like this:
function myFunc(varNames, conditions){
if(varNames.length){
var name = varNames.shift(),
condition = conditions.shift();
db.get(name, function(val){
if(isGood(val, condition)){
myFunc(varNames, conditions);
}
});
} else {
doStuff();
}
}

Categories

Resources