How to run async in consecutive order in angular [duplicate] - javascript

This question already has answers here:
How can I execute array of promises in sequential order?
(9 answers)
Closed 5 years ago.
$scope.SaveAuditItems = function (audit) {
var isError = false;
$scope.isProcessing = true;
var defer = $q.defer();
var promises = [];
for (var siteCounter = 0; siteCounter < audit.SessionSiteList.length; siteCounter++) {
for (var itemCounter = 0; itemCounter < audit.SessionSiteList[siteCounter].AuditItemList.length; itemCounter++) {
var item = audit.SessionSiteList[siteCounter].AuditItemList[itemCounter];
item.TotalItems = audit.SessionSiteList[siteCounter].AuditItemList.length;
item.CurrentItem = itemCounter;
promises.push($scope.submitaudit(item));
}
};
//It appears to be running all the promises in the array in parrallel then running CloseAudit.
$q.all(promises).then(
function (response) {
$scope.CloseAudit(audit.AuditSessionId).then(function () {
},
function(){
alert("done");
},
function(){
alert("done");
}
);
}).catch(function (exception) {
$scope.Loading("Could not submit audit session #'+ audit.AuditSessionId +' , please try again.", 2000);
});
}
How do make the promises run in consecutive order? It causes race conditions on the server data is being submitted to? This angular 1 code.
How do i use then when i do not know how many promises i will be running in sequence? All other answers use then but are always predefined count of promises. I cannot have a then within a then for ever..i can't seem to comprehend how i do this.
-------------------------------------edit 2---------------------------------------------
$scope.SaveAuditItems = function (audit) {
$ionicLoading.show({
template: '<i class="icon ion-loading-c"></i>Please wait..Sending Item ( 1 Of ' + $scope.AuditItemCounter + ' )',
}).then(function () {});
var isError = false;
$scope.isProcessing = true;
var defer = $q.defer();
var promises = [];
for (var siteCounter = 0; siteCounter < audit.SessionSiteList.length; siteCounter++) {
for (var itemCounter = 0; itemCounter < audit.SessionSiteList[siteCounter].AuditItemList.length; itemCounter++) {
var item = audit.SessionSiteList[siteCounter].AuditItemList[itemCounter];
item.TotalItems = audit.SessionSiteList[siteCounter].AuditItemList.length;
item.CurrentItem = itemCounter;
// $scope.Loading('Sending Item ( ' + item.CurrentItem + ' Of ' + item.TotalItems + ' )..', 0).then(function () {
promises.push($scope.submitaudit(item));
ConsoleLogger.AddLog('Sent Item ( ' + item.CurrentItem + ' Of ' + item.TotalItems + ' )');
//Loading.show({ template: '<i class="icon ion-loading-c"></i>Sending Item ' + item.CurrentItem + ' of ' + item.TotalItems + ' ', }).then(function () { });
$scope.Loading('Sent item ( ' + item.CurrentItem + ' Of ' + item.TotalItems + ' )', 0).then(function () {});
}
}
var all = promises.reduce(function (cur, next) {
return cur.then(next);
}, Promise.resolve(true));
all.then(function (a) {
$ionicLoading.show({
template: '<i class="icon ion-loading-c"></i>Finalising your audit..'
}).then(function () {
$scope.CloseAudit(audit.AuditSessionId).then(function () {
ConsoleLogger.AddLog('Audit submitted successfully.');
});
},
function () {
alert("doneeee");
},
function () {
alert("done");
}
);
});
You can see in the timing that the promises are not running in sequence as expected? What am i missing? I made the promises with a timeout of 7 seconds..
The closeaudit should have run after all the promises had returned but not happening for me!
GOT IT WORKING BY CHNAGING
promises.push($scope.submitaudit(item));
TO
promises.push(function(){return $scope.submitaudit(item)});

Here is a quick example how to do it. Reduce all promises from the array one by one, the reduce function will chain then() of each promise. It will start with first after the first has resolved it will call second and so on ... to the end ;].
var promises = [
function() { return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('resolve promise 1 after 3sec');
resolve('promise 1');
}, 3000)
})},
function() { return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('resolve promise 2 after 1.5sec');
resolve('promise 2');
}, 1500)
})},
function() { return new Promise(function(resolve, reject) {
setTimeout(function() {
console.log('resolve promise 3 after 2sec');
resolve('promise 3');
}, 2000);
})}];
var all = promises.reduce(function(cur, next) {
return cur.then(next);
}, Promise.resolve(true));
all.then(function(a) {
console.log('all are done!');
});

Related

Firebase clearTimeout not stop the setTimeout

How can I stop a setTimeout function before starting from the onWrite event on a "Firebase Cloud Function"? I try with setTimeout, but the clearTimeout do not stop the setTimeout.
P.S.: I've already raised the script timeout from the Firebase panel from 60 seconds (default) to 360 seconds.
var timeOutOnline = {};
exports.online = functions.database.ref('/server/{serverId}/online/time').onWrite(event => {
const serverId = event.params.serverId;
var time = event.data.val();
var lastDateOnline = new Date( time * 1000);
var dateString = lastDateOnline.toGMTString();
clearTimeout(timeOutOnline[serverId]);
refGet = db.ref('/server/'+serverId+'/online/');
refGet.once('value').then(function(snapshot) {
var onlineSnap = snapshot.val();
if (onlineSnap && onlineSnap.alarm == true) {
sendMail(serverId,false, 'not responding' , 'Server '+ serverId + ' not responding from '+dateString);
setAlarmStatus(serverId,false);
} else {
timeOutOnline[serverId] = setTimeout(function() {
sendMail(serverId,true, 'not responding' , 'Server '+ serverId + ' not responding from '+dateString);
setAlarmStatus(serverId,true);
}, 21000);
}
});
return true;
});
UPDATE:
Now I use promise, but clearTimeout still not working, not clearing setTimeout function. What am I doing wrong?
var timeOutOnline = {};
exports.online = functions.database.ref('/server/{serverId}/online/time').onWrite(event => {
const serverId = event.params.serverId;
var time = event.data.val();
var lastDateOnline = new Date( time * 1000);
var dateString = lastDateOnline.toGMTString();
console.log(v);
if(!timeOutOnline[serverId]) {
timeOutOnline[serverId] = [];
} else {
timeOutOnline[serverId].length;
for (var i = 0; i < timeOutOnline[serverId].length; i++) {
if (timeOutOnline[serverId][i]) {
timeOutOnline[serverId][i].cancel();
timeOutOnline[serverId][i] = null;
}
}
}
timeOutOnline[serverId] = timeOutOnline[serverId].filter(n => n);
refGet = db.ref('/server/'+serverId+'/online/');
refGet.once('value').then(function(snapshot) {
var onlineSnap = snapshot.val();
if (onlineSnap && onlineSnap.alarm == true) {
sendMail(serverId,false, 'not responding' , 'Server '+ serverId + ' not responding from '+dateString);
setAlarmStatus(serverId,false);
} else {
timeOutOnline[serverId].push(waitForServer(210000));
var index = timeOutOnline[serverId].length - 1;
return timeOutOnline[serverId][index].promise.then(function(i) {
console.log('Server '+ serverId + ' not responding from '+dateString);
sendMail(serverId,true, 'not responding' , 'Server '+ serverId + ' not responding from '+dateString);
setAlarmStatus(serverId,true);
});
}
});
return true;
});
function waitForServer(ms) {
var timeout, promise;
promise = new Promise(function(resolve, reject) {
timeout = setTimeout(function() {
resolve('timeout done');
}, ms);
});
return {
promise:promise,
cancel:function(){
console.log('cancel timeout: '+timeout);
clearTimeout(timeout);
}
};
}
There's a lot of things wrong in this function.
First of all, it's not clear to me why you want to use setTimeout() in this function. In fact, there are almost no valid use cases for setTimeout() with Cloud Functions. Functions are supposed to execute as fast as possible, and setTimeout only really just incurs extra cost to your processing. Cloud Functions does not enable you to run "background work" that survives past the invocation of the function - that's just not how it works.
Second of all, you're performing async work here, but you're not returning a promise to indicate when that work completes. If you don't return a promise that's resolved when your work completes, your function is highly likely to exhibit strange problems, including work not completing as you'd expect.

Why isn't my promise getting resolved?

I have two functions - A helper function for downloading files which is as follows
var downloadHelper = function(url, saveDir) {
var deferred = Q.defer();
setTimeout(function() {
deferred.resolve("success");
}, 2000);
return deferred.promise;
}
Now I have a list of files to be downloaded in parallel. I have the logic for that function as follows:
var downloadAll = function() {
var fileDownloadList = []
for(var key in config.files) {
var deferred = Q.defer();
var saveLocation = __base + config.localDir
downloadHelper(
config.files[key],
saveLocation
).then(function() {
deferred.resolve("downloaded: " + fileUrl);
}).catch(function(err) {
deferred.reject(err);
});
fileDownloadList.push(deferred.promise);
}
Q.all(fileDownloadList).done(function() {
console.log("All downloaded");
},function(err) {
console.log(err);
});
setTimeout(function() {
console.log(fileDownloadList);
}, 10000);
}
The done is never getting called!
For debugging purposes, I added a setTimeout that will be called after 10 seconds and what I see is that out of 2 files, the second promise is resolved and the first one is still in pending state.
Any ideas?
Thanks in advance
One way to make your code work
for(var key in config.files) {
(function() {
var deferred = Q.defer();
var saveLocation = __base + config.localDir
downloadHelper(
config.files[key],
saveLocation
).then(function() {
deferred.resolve("downloaded: " + fileUrl);
}).catch(function(err) {
deferred.reject(err);
});
fileDownloadList.push(deferred.promise);
}());
}
But since downloadhelper returns a promise, no need to create yet another one
for (var key in config.files) {
var saveLocation = __base + config.localDir
fileDownloadList.push(downloadHelper(
config.files[key],
saveLocation
).then(function () {
return("downloaded: " + fileUrl);
}));
}
You'll see I removed
.catch(function(err) {
deferred.reject(err);
})
That's redundant, it's the same as not having the catch at all

Understanding chaining of sequential asynchronous operations using jquery deferred and then

I have been trying to wrap my head around jquery deferred and then functions. As I gather from jQuery then documentation, the then function sends the return value of the callback to the next then handler if they are so chained. Given that, why is my code not working as expected?
function log(message) {
var d = new Date();
$('#output').append('<div>' + d.getSeconds() + '.' + d.getMilliseconds() + ': ' + message + '</div>');
}
function asyncWait(millis) {
var dfd = $.Deferred();
setTimeout(function () {
var d = new Date();
log('done waiting for ' + millis + 'ms');
dfd.resolve(millis);
}, millis);
return dfd.promise();
}
function startTest0() {
return asyncWait(1000).then(asyncWait).then(asyncWait).then(asyncWait).done(function () {
log('all done, 4 times');
});
}
function startTest() {
asyncWait(500).then(function () {
return asyncwait(1000);
}).then(function () {
return asyncWait(1500);
}).then(function () {
return asyncWait(2000);
}).done(function () {
log('all done');
});
}
log('welcome');
log('starting test ...');
startTest0().done(function() { log('starting the second test'); startTest(); });
JS Fiddle here: Sample code. I was expecting a similar behavior in both tests but something eludes me. What am I missing?
Thanks in advance!
EDIT: See an updated DEMO where I am trying to chain the async operations to start after the previous one is done.
Except for one typo (asyncwait instead of asyncWait) your code works. Check below.
function log(message) {
var d = new Date();
$('#output').append('<div>' + d.getSeconds() + '.' + d.getMilliseconds() + ': ' + message + '</div>');
}
function asyncWait(millis) {
var dfd = $.Deferred();
setTimeout(function () {
var d = new Date();
log('done waiting for ' + millis + 'ms');
dfd.resolve(millis);
}, millis);
return dfd.promise();
}
function startTest0() {
return asyncWait(1000).then(asyncWait).then(asyncWait).then(asyncWait).done(function () {
log('all done, 4 times');
});
}
function startTest() {
asyncWait(500).then(function () {
return asyncWait(1000);
}).then(function () {
return asyncWait(1500);
}).then(function () {
return asyncWait(2000);
}).done(function () {
log('all done');
});
}
log('welcome');
log('starting test ...');
startTest0().done(function() { log('starting the second test'); startTest(); });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>
Lesson to learn: Put any JS code through jshint before and after you fix bugs.
As i can see here, you are calling startTest0 function returning its promise object and calling then callback without returning new times into next then callback. I modified your startTest() into this :
function startTest() {
return asyncWait(500).then(function () {
asyncWait(1000);
return 1500; // here we pass to the next then
}).then(function (ms) { // ms here we got 1500
asyncWait(ms);
return 2000; // here we pass to the next then
}).then(function (ms) { // ms here we got 2000
asyncWait(ms)
return asyncWait(2500);
}).done(function () {
log('all done');
});
}
DEMO

stoping async tasks running in parallel

I'm trying to run through (using foreach) an array of objects and then for each I'd like to call a function that uses request to get a file and then unzips it with zlib, but one at a time, given the nature of node this is currently done asynchronously.
I'd like it to be done something like this...
- foreach - first object
- call function for first object
- when function has completed
- go to the next object in the array
I have tried using the SYNC module to try and solve this but with no luck.
Any ideas on how I can achieve this?
// the function i am trying to run for each in sync
var downloadUnzipFile = function(mID) {
try {
// Read File
console.log("Started download/unzip of merchant: " + mID + " # " + new Date().format('H:i:s').toString());
request(linkConst(mID))
// Un-Gzip
.pipe(zlib.createGunzip())
// Write File
.pipe(fs.createWriteStream(fileName(mID)))
.on('error', function(err) {
console.error(err);
})
.on('finish', function() {
console.log("CSV created: " + fileName(mID));
console.log("Completed merchant: " + mID + " # " + new Date().format('H:i:s').toString());
//console.log("Parsing CSV...");
//csvReader(fileName);
});
} catch (e) {
console.error(e);
}
}
module.exports = function(sMerchants) {
var oMerchants = JSON.parse(JSON.stringify(sMerchants));
sync(function() {
oMerchants.forEach(function eachMerchant(merchant) {
downloadUnzipFile(merchant.merchant_aw_id);
})
})
};
var promiseQueue = (function() {
'use strict';
var promiseQueue = function() {
var queue = [Promise.resolve(true)];
var add = function(cb) {
var args = Array.prototype.slice.call(arguments);
args.shift();
queue.unshift(new Promise(function(resolve) {
queue[0].then(function() {
resolve(cb.apply(null, args));
queue.pop();
});
}));
};
return {
add: add
}
}
return promiseQueue;
}());
usage EXAMPLE:
This is the asynch function that will be called
var theFun = function (time, n) { // use whatever arguments you like that will be called with your function
return new Promise(function(resolve) {
//asynch function goes here INSTEAD of the setTimeout and it's contents, I repeat, INSTEAD of the setTimeout
setTimeout(function() { // this is for demonstrating ONLY
console.log('resolving', n, time); // this is for demonstrating ONLY
resolve(time); // this is for demonstrating ONLY
}, time); // this is for demonstrating ONLY
// remember to resolve("someValueNotImportantAsItIsntUsedAnywhere") on completion of your asynch function
});
}
This is how the items get added to the queue - I did it this way because of MY use case
var pq = promiseQueue();
for(var i = 0; i < 5; i++ ) {
var r = 1000 - i * 150;
console.log('adding ', i, r);
pq.add(theFun, r, i);
}
Hope you find this of some use
First, your function needs to take a callback so it can communicate when it has finished:
var downloadUnzipFile = function(mID, next) {
try {
// Read File
console.log("Started download/unzip of merchant: " + mID + " # " + new Date().format('H:i:s').toString());
request(linkConst(mID))
// Un-Gzip
.pipe(zlib.createGunzip())
// Write File
.pipe(fs.createWriteStream(fileName(mID)))
.on('error', function(err) {
console.error(err);
})
.on('finish', function() {
console.log("CSV created: " + fileName(mID));
console.log("Completed merchant: " + mID + " # " + new Date().format('H:i:s').toString());
//console.log("Parsing CSV...");
//csvReader(fileName);
next();
});
} catch (e) {
console.error(e);
next();
}
}
Then, we need to recursively call each one when the previous has finished:
module.exports = function(sMerchants, next) {
var oMerchants = JSON.parse(JSON.stringify(sMerchants));
var i = 0;
var run = function() {
if(i < oMerchants.length)
downloadUnzipFile(i++, run);
else
next();
};
};
Note that I also added a callback to the exported function, so it can communicate when it is finished. If this is unnecessary, you can drop it.
This may work for you, uses Promise. Need to add resolve and reject callbacks to your downloadUnzipFile-
var exports = (function () {
'use strict';
var pre = document.getElementById('out');
function log(str) {
pre.appendChild(document.createTextNode(str + '\n'));
}
function downloadUnzipFile(id, resolve, reject) {
log('Start: ' + id);
try {
setTimeout(function () {
resolve(id);
}, 3000);
} catch (e) {
reject(e);
}
}
function done(id) {
log('Done: ' + id);
}
function error(e) {
log(e.message);
}
function getPromise(mID) {
return new Promise(function (resolve, reject) {
downloadUnzipFile(mID, resolve, reject);
});
}
return function (sMerchants) {
JSON.parse(sMerchants).reduce(function (next, mID) {
if (!next) {
next = getPromise(mID);
} else {
next = next.then(function (id) {
done(id);
return getPromise(mID);
}, error);
}
return next;
}, null).then(done, error);
};
}());
exports(JSON.stringify([1, 2, 3, 4, 5]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/json2/20150503/json2.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.1.7/es5-shim.min.js"></script>
<script src="https://rawgit.com/jakearchibald/es6-promise/master/dist/es6-promise.min.js"></script>
<pre id="out"></pre>
I added the browser shims to support older browsers that may be viewing this, you shouldn't need them on node.js but you may need to require a Promise shim if you are using an old node.js.

asynchronous execution in protractor end to end tests

I have written a function , which is returning a value. In my main i am calling the function like this:
var fn_return_val = lesson.select_lesson(lesson1_text);
console.log("returned value is : " + fn_return_val);
And my function implementation is like(other file.js) :
module.exports = {
select_lesson:
function select_lesson(lesson_name) {
console.log('\n ************************* Lessson name: ' + lesson_name);
var desiredOption, status;
var repeter = element.all(by.repeater('item in items'));
repeter.then(function (items) {
items.forEach(function (icon) {
console.log('\n ************************* item');
icon.getText().then(function (txt) {
if (txt == lesson_name) {
desiredOption = icon;
}
})
}).then(function clickOption() {
if (desiredOption) {
var el = desiredOption.all(by.css('[ng-click="launchActivity()"]'));
var el_progress = desiredOption.all(by.css('.pna-progress'));
var abc = el.getAttribute('value').then(function (txt) {
status = txt;
return status
});
el_progress.getAttribute('style').then(function (progress) {
console.log('\n ************************* Lessson progress : ' + progress);
});
el.click();
}
});
});
}
};
The problem is function is returning "undefined" value, and the print statement console.log("returned value is : " + fn_return_val);
is executing before the function implementation
Can anyone help me on resolving this?
This is all about promises and protractor's Control Flow.
You need to resolve the promise and log the results inside then:
lesson.select_lesson(lesson1_text).then(function(fn_return_val) {
console.log("returned value is : " + fn_return_val);
});
And you also need to return from a function:
function select_lesson(lesson_name) {
...
// return here
return repeter.then(function (items) {
...
}).then(function clickOption() {
...
});
});
}

Categories