I have an Object, InsertDB, that contains multiple functions. I would like to execute one after the other.
Only execute function 2 InsertDB.addNot(); when function 1 InsertDB.addBk(); has completely finished looping, inserting records, and so on.
// Object with 7 functions to be called. Each performs a loop and insert records into IndexedDB
InsertDB = {
addBk: function(Object) {
if (Object.hasOwnProperty("Books")) {
for (var i = 0, j = Object["Books"].length; i < j; i++) {
server.Books.add({
title: Object["Books"][i].id,
content: Object["Books"][i]
});
}
}
},
addNot: function(Object) {
if (Object.hasOwnProperty("Notifications")) {
for (var i = 0, j = Object["Notifications"].length; i < j; i++) {
server.Notifications.add({
content: Object["Notifications"][i]
});
}
}
} etc...
}
//On Ajax success event, run above functions one after the other as described above.
Synchronize = {
Start: function(){
return $.ajax({
......
success: function(data){
var Object = $.parseJSON(data);
InsertDB.addBk(Object);
InsertDB.addNot(Object);
InsertDB.addUser(Object);
InsertDB.addHistory(Object); ect...
}
}};
Synchornize.Start();
You should implement the functions so that they return promises, and then you can subscribe to those promises. You can use jQuery or q.js for that.
As Tommi suggeted, I had to ensure that the function returns promises then I can fire an event when all iterations are finished, I did do as follow :
InsertDB = {
addBk: function(Object) {
if (Object.hasOwnProperty("Books")) {
var promises = [],p;
for (var i = 0, j = Object["Books"].length; i < j; i++) {
p = server.Books.add({
title: Object["Books"][i].id,
content: Object["Books"][i]
});
promises.push(p);
}
$.when.apply($, promises).done(function() {
// callback function when all iterations are finished
});
}
}
Related
I have this multiple promise request that needs to be resolved before continuing next set of requests.
If the printItemList length is 1, it works fine. But the problem starts hapenning when its more than 2.
The idea is to fire print_label once the previous print_label is resolved successfully. Right now its getting fired immediately and not waiting for previous request to finish.
Is there anything I'm doing wrong here.
Any help on this would be greatly appreciated.
function print_batch(printItemList){
var chain = $.when();
var arr = [];
for(var batch_size = 0; batch_size < printItemList.length; batch_size ++){
(function(i){
chain = chain.then(function() {
$.when.apply($, arr).done(function(){
arr = print_label(printItemList[i]);
});
});
})(batch_size);
}
}
function print_label(selectedRow) {
var d = $.Deferred();
var chain = $.when();
var arr = [];
var request = buildLabel(selectedRow);
var noOfLabel = parseInt(selectedRow.labelCount);
var url = 'API_URL';
var epos = new epson.ePOSPrint(url);
for (var count = 0; count < noOfLabel; count++) {
(function(i){
chain = chain.then(function() {
var def = sendRequest(selectedRow, epos, request);
arr.push(def);
return def;
});
})(count);
}
return arr;
}
function sendRequest(selectedRow, epos, request){
var deferred = $.Deferred();
epos.send(request);
epos.onreceive = function(res) {
return deferred.resolve(res);
}
epos.onerror = function(err) {
return deferred.reject();
}
return deferred.promise();
}
function print_batch(printItemList){
//create recursive method
function print_next_label(index) {
//if we have not reached the end of the list, do more work
if (index < printItemList.length) {
//call the method, wait for all the promises to finish
$.when.apply($, print_label(printItemList[index])).done(function(){
//now that they are done, try to print the next label
print_next_label(++index);
});
}
}
//start the recursive method loop with index 0
print_next_label(0);
}
Here is my code :
for (var i = 0; i < list.length; i++) {
$.when(
$.ajax({url: "./dorequest.php?id=" + list[i],
success: function(response){ jsonFriendlist = response; }}) ).done(
function(jsonFriendlist) {
var friendListObject = JSON.parse(jsonFriendlist);
if (!jQuery.isEmptyObject(friendListObject)) {
var rawListFriend = friendListObject.friendslist.friends;
for (var j = 0; j < rawListFriend.length; j++) {
console.log(i);
playerLinkList[i].listFriends.push(rawListFriend[j].id);
}
}
}
);
}
Basicly I try to update a part of an object in the array list sending a request by item i that array. But the code fail because every call to the "done" function i done with i = 13 (which is list.lenght).
My understanding is that since the request take time to be send an retrieve the result and since it's done async when the done function is call the main thread is already out of the forso i = 13.
My question is how can I manage to "freeze" i by passing it by copy when the ajax request is sent ?
Thanks,
Using a closure solved the issue.
for (var i = 0; i < playerIds.length; i++) {
// Pass the parameter "i" to the done functions
(function(index) {
var dfd = $.ajax({url: "./dorequest.php?id=" + playerIds[i] + "&requesttype=friendlist",
success: function(response){ jsonFriendlist = response; }});
dfd.done(
function(jsonFriendlist) {
var friendListObject = JSON.parse(jsonFriendlist);
if (!jQuery.isEmptyObject(friendListObject)) {
var rawListFriend = friendListObject.friendslist.friends;
for (var j = 0; j < rawListFriend.length; j++) {
playerLinkList[index].listFriends.push(rawListFriend[j].steamid);
}
}
}
);
})(i);
}
I am creating a 'drag and drop' field which can accept files and folders recursively. This is the code:
function traverseFileTree(item) {
if (item.isFile) {
item.file(function(file) {
var reader = new FileReader();
reader.onload = function(evt) {
// do something...
};
reader.readAsText(file);
});
} else if (item.isDirectory) {
var dirReader = item.createReader();
dirReader.readEntries(function(entries) {
for (var i = 0; i < entries.length; i++) {
traverseFileTree(entries[i]);
}
});
}
}
var dropZone = document.getElementById("drop_zone");
dropZone.addEventListener("drop", function(evt) {
var items = event.dataTransfer.items;
for (var i = 0; i < items.length; i++) {
var item = items[i].webkitGetAsEntry();
if (item) {
traverseFileTree(item);
}
}
}, false);
My question: what is the best or perhaps the most elegant way of having a callback upon reading the last file. Since the read is asynchronous, I can not just rely on scope rules. So, is it by use of a counter, or am I missing some cool method here? Thanks!
You could make some sort of counter and check it on the onload functions (you may want to have it in the onerror, too).
function CountDown() {
this.remain = 0;
}
CountDown.prototype.inc = function (x) {
if (undefined === x) x = 1;
this.remain += +x;
return this;
}
CountDown.prototype.done = function () {
if (0 >= --this.remain) {
this.remain = 0;
return true;
}
return false;
}
var count = new CountDown().inc(3); // 3 files
// in the onload for each, test
count.done(); // false
count.done(); // false
count.done(); // true
count.done(); // true
count.done(); // true, etc
Of course, this could also be done in your code without the whole object wrapping if you scope the variable properly.
I have 3 methods
exports.getImageById = function (resultFn, id) {
...
}
exports.getCollectionById = function (resultFn, id) {
}
in the third method I want to call both methods
exports.getCollectionImages = function (resultFn, collectionId) {
var arr = new Array();
this.getCollectionById( // fine, 1st call
function (result) {
var images = result.image;
for (i = 0; i < images.length; i++) {
this.getImageById(function (result1) { // error, 2nd call
arr[i] = result1;
}, images[i]
);
}
}
, collectionId
);
resultFn(arr);
}
I can call first function this.getCollectionById but it fails to call this.getImageById, it says undefined function, whats the reason for that?
When you call this.getCollectionById passing it a callback, the callback doesn't have access to the same this
The simplest solution is to save this as a local variable.
exports.getCollectionImages = function (resultFn, collectionId) {
var arr = new Array();
var me = this; // Save this
this.getCollectionById( // fine, 1st call
function (result) {
var images = result.image;
for (var i = 0; i < images.length; i++) {
// Use me instead of this
me.getImageById(function (result1) { // error, 2nd call
arr[i] = result1;
}, images[i]);
}
}, collectionId);
resultFn(arr);
}
The value of this inside the inner function is not the same object as outside, because it's determined depending on how the function is called. You can find a detailed explanation in the MDN article on this.
One of the ways to solve it is by keeping a reference to the outer this in another variable such as that:
var that = this;
this.getCollectionById( // fine, 1st call
function (result) {
var images = result.image;
for (i = 0; i < images.length; i++) {
that.getImageById(function (result1) { // 2nd call
arr[i] = result1;
}, images[i]
);
}
}
, collectionId
);
I want to run window.resolveLocalFileSystemURI(file,success,fail) in for loop passing different file entries and want to return resolved entries in array only after I get all the entries.
function resolveFiles(result,callback)
{
var resultData=[]
window.resolveLocalFileSystemURI(result, function(entry)
{
resolvedGalleryImages.push(entry);
callback(resolvedGalleryImages);
resolvedGalleryImages=[];
}, function(e)
{
alert("err"+e);});
}
//calling--
//#filesarr has captured images uris
for(i = 0; i < filesarr.length; i++)
{
resolveFiles(filesarr[i],function(result){
var resultArr = result;
});
}
How can I prevent callback to be called before I get all the entries.
There are multiple primary ways to attack a problem like this:
Manual coding of an asynchronous loop
Using promises to coordinate multiple async operations
Using a library like Async to coordinate multiple async operations
Here's the manual version:
function getFiles(filesarr, doneCallback) {
var results = new Array(filesarr.length);
var errors = new Array(filesarr.length);
var errorCnt = 0;
var overallCnt = 0;
function checkDone() {
if (overallCnt === filesarr.length) {
if (errorCount) {
doneCallback(errors, results);
} else {
doneCallback(null, results);
}
}
}
for (var i = 0; i < filesarr.length; i++) {
(function(index) {
window.resolveLocalFileSystemURI(url, function (entry) {
results[index] = entry;
++overallCnt;
checkDone();
}, function (e) {
errors[index] = e;
++errorCount;
++overallCnt;
checkDone();
});
})(i);
}
}
getFiles(filesarr, function(errArray, results) {
if (errArray) {
// go errors here
} else {
// process results
}
});
And, here's a version that uses ES6 promises:
// make a promisified function
function resolveFile(url) {
return new Promise(function(resolve, reject) {
window.resolveLocalFileSystemURI(url, resolve, reject);
});
}
function getFiles(filesarr) {
var promises = [];
for (var i = 0; i < filesarr.length; i++) {
promises.push(resolveFile(filesarr[i]));
}
return Promise.all(promises);
}
getFiles(filesarr).then(function(results) {
// process results here
}, function(err) {
// error here
});
this is all based on all your functions being synchronous, and if not: be more specific what you're using. (there is no jQuery here yet your tags say jquery)
function resolveFile(path) {
var result;
window.resolveLocalFileSystemURI(path, function(file) {
result = file;
});
return file;
}
var resolvedFiles = filesarr.map(resolveFile);
callback(resolvedFiles);