AngularJS: how to call recursive function with a delay? - javascript

I have such function:
$scope.getContactsByScroll = function() {
$scope.pageN = $scope.pageN + 1;
if (!$scope.allDataIsLoaded){
fetchMyDataService.getContactsByScrollService($scope.pageN).then(function(response) {
if (response.length === 0){
$scope.allDataIsLoaded = true;
}
else if (response.length !== 0 && !$scope.allDataIsLoaded){
angular.forEach(response, function(el) {
$scope.contacts.push(el);
});
//$timeout(function() {
$scope.getContactsByScroll();
//}, 2000);
}
}).catch(function() {
$scope.allDataIsLoaded = true;
});
}
};
but it call themselves several times even, if $scope.allDataIsLoaded is false
when i set timeout: all works like a charm. But i don't think that this is a good solution. How can i delay my function without a timeout?

Using timeout in asynchronous functions is not a good idea:
If your request time is longer then timeout, then you'll get unnecessary requests.
If your timeout is bigger then request time, then you'll get unnecessary lags.
You should use promise chain for reqursive requests. Try something like this:
var asyncService = function ($timeout)
{
var someData = 10;
var service =
{
FetchData: function (someArray)
{
someData++;
//timeout here is just for demonstration of async request
return $timeout(function () { return someData }, 1000)
.then(function (result)
{
return service.ProcessData(result, someArray);
});
},
ProcessData: function (data, someArray)
{
console.log(data);
if (data == 15)
{
return someArray;
}
else
{
someArray.push(data)
return service.FetchData(someArray);
}
}
};
return service;
}
Here's a plunker with demonstration

Related

Function returning before jQuery promise within promise is resolved

I have a function which calls dealCardSelectableAI(), which sets up a number of jQuery deferred promises. The function setCardName() is then called from within it. Once both functions complete their tasks saveGame() should then be triggered.
Everything works, except setCardName() does not complete before saveGame() is triggered. It appears that deferredQueue.push(setCardName(system, result)); is not operating as I expected. I am not sure where I'm going wrong or how to resolve the issue.
var setCardName = function (system, card) {
var deferred = $.Deferred();
require(["cards/" + card[0].id], function (data) {
var cardName = loc(data.summarize());
system.star.ai().cardName = ko.observable(cardName);
deferred.resolve();
});
return deferred.promise();
};
var dealCardSelectableAI = function (win, turnState) {
var deferred = $.Deferred();
// Avoid running twice after winning a fight
if (!win || turnState === "end") {
var deferredQueue = [];
_.forEach(model.galaxy.systems(), function (system, starIndex) {
if (
model.canSelect(starIndex) &&
system.star.ai() &&
system.star.ai().treasurePlanet !== true
) {
deferredQueue.push(
chooseCards({
inventory: inventory,
count: 1,
star: system.star,
galaxy: game.galaxy(),
addSlot: false,
}).then(function (result) {
deferredQueue.push(setCardName(system, result));
system.star.cardList(result);
})
);
}
});
$.when(deferredQueue).then(function () {
deferred.resolve();
});
} else {
deferred.resolve();
}
return deferred.promise();
};
dealCardSelectableAI(false).then(saveGame(game, true));
Your code says call saveGame() and what is returned from the function call should be set to then. It is not saying, "call saveGame when done"
dealCardSelectableAI(false).then(function () { saveGame(game, true) });

Completing Promises after runs/waitsFor in Jasmine 1.3

I'm using jasmine-node 1.14.5, which underneath uses jasmine 1.3, and I'm having issues getting runs/waitFor to work properly with Promises.
In certain tests, I'd like to runs/waitFor to wait for a particular condition to happen, and when it occurs, fulfil a Promise that I return back. However, the moment I attempt to construct a Promise passing in the function(success, fail) parameter, none of the code inside the runs/waitFor gets called. However, if the Promise is resolved directly, it works. Any idea the former option does not work?
To give some examples, the following works fine:
it("should support async execution of test preparation and expectations", function(done) {
var p = Promise.resolve("boo")
.then(function() {
var p2 = Promise.resolve("whatever");
runs(function() {
flag = false;
value = 0;
intId = setInterval(function() {
console.log(value);
if (++value == 3) { clearInterval(intId); flag = true; }
}, 500);
});
waitsFor(function() {
return flag;
}, "The Value should be incremented", 5000);
runs(function() {
expect(value).toEqual(3);
});
return p2;
});
p.then(function() {
done();
}).catch(function(err) {
done(err);
});
});
But on the other hand, this does not work because although runs/waitsFor are called without problems, the callbacks inside do not:
it("should support async execution of test preparation and expectations", function(done) {
var p = Promise.resolve("boo")
.then(function() {
return new Promise(function (fulfil, reject) {
runs(function () {
flag = false;
value = 0;
intId = setInterval(function () {
console.log(value);
if (++value == 3) {
clearInterval(intId);
flag = true;
}
}, 500);
});
waitsFor(function () {
return flag;
}, "The Value should be incremented", 5000);
runs(function () {
expect(value).toEqual(3);
fulfil();
});
});
});
p.then(function() {
done();
}).catch(function(err) {
done(err);
});
});
I've also tried the following in the off chance but does not work either, it behaves the same way as the previous example:
it("should support async execution of test preparation and expectations", function(done) {
var p = Promise.resolve("boo")
.then(function() {
var outerFulfil;
var outerReject;
var p2 = new Promise(function(fulfil, reject) {
outerFulfil = fulfil;
outerReject = reject;
});
runs(function() {
flag = false;
value = 0;
intId = setInterval(function() {
console.log(value);
if (++value == 3) { clearInterval(intId); flag = true; }
}, 500);
});
waitsFor(function() {
return flag;
}, "The Value should be incremented", 5000);
runs(function() {
expect(value).toEqual(3);
outerFulfil();
});
return p2;
});
p.then(function() {
done();
}).catch(function(err) {
done(err);
});
});
Any idea how to solve it? Although the first example works, it does not behave the way I want because I only want the promise to be fulfilled once the assertions after the waitsFor have been carried out.
Cheers,
Galder
I ended up ditching runs/waitsFor altogether, and instead do a time based recursive Promise loop, e.g.
function waitUntil(expectF, cond, op) {
var now = new Date().getTime();
function done(actual) {
return cond(actual)
&& new Date().getTime() < now + MAX_WAIT;
}
function loop(promise) {
exports.sleepFor(100); // brief sleep
// Simple recursive loop until condition has been met
return promise
.then(function(response) {
return !done(response)
? loop(op())
: response;
})
.catch(function() {
return loop(op());
});
}
return loop(op())
.then(function(actual) {
expectF(actual);
});
}
op is an operation that returns a Promise.
For example, I wanted to wait until certain number of nodes have joined a cluster:
function waitUntilView(expectNumMembers, nodeName) {
return waitUntil(
function(members) { expect(members.length).toEqual(expectNumMembers); },
function(members) { return _.isEqual(expectNumMembers, members.length); },
getClusterMembers(nodeName)
);
}

How to structure javascript callbacks in a chain of dependant functions?

I have only used simple callbacks, i.e. function that performs async AJAX call and calls back once done. Whenever things got anymore complicated I have used $.Deferred(). The problem is that handling promises is a lot of code every time, i would like to know how to use callbacks correctly instead.
Here is my example. (the problem arises in the return from secondary()):
function primary()
{
//call secondary with self as callback
var data = secondary("someStr", primary);
if (data !== false) {
//logic that uses data
}
}
function secondary(str, callback)
{
//call the third function. Since we need parameters
//on the callback, we create anon function
var returnFromThird = tertiary(function() {
secondary(str, callback);
});
if (returnFromThird !== false) {
//when we have data from third do someting....
//but here lies the problem, how do i callback primary()?
return returnFromThird + " " + str;
} else {
//third is not yet ready
return false;
}
}
var myVar3 = null;
function tertiary(callback)
{
if (myVar3 === null) {
//do async ajax call that sets myVar3 value
var ajaxRequest = $.ajax({
url: "/someUrl",
type: "POST",
data: {myData : "blabla"},
async: true,
});
ajaxRequest.done(function(data) {
myVar3 = data;
//issue the call back, so secondary() can come get myVar3
callback();
});
//we did not have the required data so we return false
return false;
} else {
return myVar3;
}
}
//execute
primary();
Here is how i would normally handle the issue using JQuery Deferred:
function primary()
{
var promise = secondary(str);
$.when(promise).then(
function (data) {
//logic that uses data
}
);
}
function secondary(str)
{
var dfd = $.Deferred();
var promise = tertiary();
$.when(promise).then(
function (data) {
dfd.resolve(data + " " + str);
}
);
return dfd.promise();
}
var myVar3 = null;
function tertiary()
{
var dfd = $.Deferred();
if (myVar3 === null) {
var ajaxRequest = $.ajax({
url: "/someUrl",
type: "POST",
data: {myData : "blabla"},
async: true,
});
ajaxRequest.done(function(data) {
myVar3 = data;
dfd.resolve(myVar3);
});
} else {
dfd.resolve(myVar3);
}
return dfd.promise();
}
primary();
If you are using callbacks, you should always call the callback, not sometimes return a value:
var myVar3 = null;
function tertiary(callback) {
if (myVar3 === null) {
//do async ajax call that sets myVar3 value
$.post("/someUrl", {myData : "blabla"}).done(function(data) {
myVar3 = data;
callback(data); // just pass the data to the callback
});
} else {
return callback(myVar3); // always call the callback with the data
}
}
Now your other functions would look like this:
function primary() {
secondary("someStr", function(data) {
//logic that uses data
});
}
function secondary(str, callback) {
tertiary(function(returnFromThird) {
callback(returnFromThird + " " + str);
})
}
But you are right, you should be using promises, it simplifies this a great lot:
var myVarPromise = null;
function tertiary() {
if (myVarPromise === null)
myVarPromise = $.post("/someUrl", {myData : "blabla"});
return myVarPromise;
}
function primary() {
return secondary("someStr").then(function(data) {
//logic that uses data
});
}
function secondary(str) {
return tertiary().then(function(returnFromThird) {
return returnFromThird + " " + str;
});
}

Q promises chaining to do things in correct order

I have a asynchronous function that needs to be called multiple times in the correct order. It's about uploading images to a server but like I said the images should be uploaded in the correct order.
My function looks like this:
function uploadImage(sku,fileName) {
console.log("Uploading image for sku="+sku+", imageName="+fileName);
var deferred = Q.defer();
var readStream = fs.createReadStream(rootPath+'/data/'+sku+"/"+fileName);
var req = request.post('http://localhost:3000/'+sku+'/upload/'+fileName);
readStream.pipe(req);
req.on('end', function() {
console.log("Image imageName="+fileName+" uploaded successfully.");
db.updateLastUploadedImage(sku,fileName).then(function (res) {
if(res) {
console.log("Table watches for sku="+sku+" updated.");
deferred.resolve(sku);
}
});
});
req.on('error',function(err) {
deferred.reject(err);
});
return deferred.promise;
}
I tried to bring it on with chaining the promises like documented in https://github.com/kriskowal/q but it's not working well. Somehow I do not come to the "then" block.
So I tried to make a recursive function but it also does not go inside the "then" block of the function call.
Only this method works but its not running through the correct order.
function uploadImages(sku) {
var promises = [];
for(var x=0; x<10; x++) {
promises.push(uploadImage(sku,(x+1)+".jpg")));
}
return Q.all(promises).then(function (res) {
return sku;
});
}
My recursive solution looks like this:
function uploadImages(sku,current,max) {
var deferred = Q.defer();
if(current<=max) {
uploadImage(sku,current+'.jpg').then(function (res) {
if(res) {
uploadImages(sku,current+1,max);
}
}, function (err) {
deferred.reject();
});
} else {
deferred.resolve(sku);
}
return deferred.promise;
}
What I'm looking for is something like this (but thats not the way to implement):
return uploadImage(sku,"1.jpg").then(function(res) {
return uploadImage(sku,"2.jpg").then(function(res) {
return uploadImage(sku,"3.jpg").then(function(res) {
return uploadImage(sku,"4.jpg").then(function(res) {
return uploadImage(sku,"5.jpg").then(function(res) {
return uploadImage(sku,"6.jpg").then(function(res) {
return uploadImage(sku,"7.jpg").then(function(res) {
return uploadImage(sku,"8.jpg").then(function(res) {
return uploadImage(sku,"9.jpg").then(function(res) {
return uploadImage(sku,"10.jpg").then(function(res) {
return sku;
});
});
});
});
});
});
});
});
});
});
So what is the best practice for my purpose?
There is no concept of "correct order" for async calls because they are just that -- asynchronous and they can end at any point.
In your the callback in Q.all(promises).then(...) you should have the responses in the order that you made them, but the order of your console logs may not be in the same order due their asynchronous nature.
In your case, you can probably do it recursively:
function uploadFiles(sku, current, max) {
return uploadImage(sku, current + '.jpg').then(function (sku) {
if (current > max) { return sku; }
return uploadFiles(sku, current + 1, max);
});
}
// use it
uploadFiles('SOME_SKU', 1, 10).then(function (sku) {
// ALL DONE!
});
Try to catch exceptions from the promise.
return Q.all(promises).then(function (res) {
return sku;
})
.catch(function (error) {
// Handle any error from all above steps
});

setTimeout issue trying to wait for execution of async

I'm new to this kind of problem in javascript and i can't fix this attempt to wait for an asynchronous call combining Angular promise objects and timeouts.
The function onTimeout seems never execute.
getAsyncContent: function (asyncContentInfos) {
var deferObj = $q.defer();
var promiseObj = deferObj.promise;
asyncContentInfos.promiseObject = promiseObj;
var blockingGuard = { done: false };
promiseObj.then(function () {
blockingGuard.done = true;
});
this.wait = function () {
var executing = false;
var onTimeout = function () {
console.log("******************** timeout reached ********************");
executing = false;
};
while (!blockingGuard.done) {
if (!executing && !blockingGuard.done) {
executing = true;
setTimeout(onTimeout, 200);
}
}
};
$http.get(asyncContentInfos.URL, { cache: true })
.then(function (response) {
asyncContentInfos.responseData = response.data;
console.log("(getAsyncContent) asyncContentInfos.responseData (segue object)");
console.log(asyncContentInfos.responseData);
deferObj.resolve('(getAsyncContent) resolve');
blockingGuard.done = true;
return /*deferObj.promise*/ /*response.data*/;
}, function (errResponse) {
var err_msg = '(getAsyncContent) ERROR - ' + errResponse;
deferObj.reject(err_msg);
console.error(err_msg);
});
return {
wait: this.wait
}
}
Client code is something like this:
var asyncVocabulary = new AsyncContentInfos(BASE_URL + 'taxonomy_vocabulary.json');
getAsyncContent(asyncVocabulary).wait();
And AsyncContentInfos is:
function AsyncContentInfos(URL) {
this.URL = URL;
this.responseData = [];
this.promiseObject;
}
$http.get returns a promise which will resolve when the call completes. Promises are a way to make asyncronous more clean and straight lined than plain old callbacks.
getAsyncContent: function (asyncContentInfos) {
return $http.get(asyncContentInfos.URL, { cache: true })
.then(function (response) {
return response.data;
}, function (errResponse) {
console.error(err_msg);
throw errResponse;
});
}
Then using it:
getAsyncContent({...}).then(function(yourDataIsHere) {
});
The nice thing about promises is that they can be easily chained.
getAsyncContent({...})
.then(function(yourDataIsHere) {
return anotherAsyncCall(yourDataIsHere);
})
.then(function(your2ndDataIsHere) {
});

Categories