How to push several function with args into an array - arrays

Hello everyone,
In a Node.js module (a custom queue) i am working on,
I am using the queue module, and I need to push into its array "jobs" several functions with an argument (an int for the estimated time)
When I start the queue, an error that says that the job is not a function.
I think I understand why, it's because the function "process" is executed when I push it. But I need to execute this process later with arguments.
my code :
module.exports = Queue
var process = require("../test/processjob")
var q = require("queue");
function Queue(options) {
if (!(this instanceof Queue)) {
return new Queue(options);
}
options = options || {}
this.queue = q();
/*
handling other options
*/
}
Queue.prototype.processJob = function() {
for (var i = 0, len = this.tasks.length; i < len; i++) {
this.queue.push(process.process(this.tasks[i].estimated_time));// <== push here
}
this.queue.start(); //<== exception here
}
Thanks a lot, and sorry for my poor english.

To push a function into an array, that you'll want to execute at a later point in time, you could wrap that function with another function, i.e.:
this.queue.push(
function(cb) {
process.process(this.tasks[i].estimated_time)
cb();
}
);// <== push here
Or using ES6
this.queue.push((cb) => {
process.process(this.tasks[i].estimated_time);
cb();
});

Related

While loop in nightwatch.js

I'm trying to run the following code (a number of steps) several times (e.g: 10 times) so that:
the user navigates to the detailed url
a random button is clicked, as per the defined var values
this is repeated 10 times
the test is then complete
I'm working with the following NightwatchJS code:
var randomEmail = faker.internet.email()
var competitionReference = ['drawing_21715','drawing_21704']
var randomCompetitionReference = competitionReference[Math.floor(Math.random()*competitionReference.length)]
module.exports = {
'navigate to homepage': function (browser) {
browser
.url('http://clickswin-stage.bellamagazine.co.uk/')
},
'select a competition': function (browser) {
browser
.useXpath()
.click('//*[#id="' + randomCompetitionReference + '"]/div/div[1]')
},
};
I've read that the best way to do this would be to use a while loop, but I'm not really sure how to set this up for my code above.
For example, if I were to use:
var i = 0
while ( i < 10) {
etc, whereabouts would I need to put this loop code within my code above?
Any help would be greatly appreciated.
One solution could be using a recursive function. Here is an example of how this could look like:
var randomEmail = faker.internet.email()
var competitionReference = ['drawing_21715', 'drawing_21704']
// var randomCompetitionReference = competitionReference[Math.floor(Math.random() * competitionReference.length)]
var randomCompetitionReference = function() {return competitionReference[Math.floor(Math.random() * competitionReference.length)]}
module.exports = {
'navigate to homepage': function (browser) {
browser
.url('http://clickswin-stage.bellamagazine.co.uk/')
},
'select a competition': function (browser, recursions) {
// Put the main code into a separat recursive function.
const doClick = function(times) {
if (times > 0) { // This is the equivalent to "while ( i < 10) {"
return browser
.useXpath()
.click('//*[#id="' + randomCompetitionReference() + '"]/div/div[1]')
.useCss()
.perform(()=>{ // perform() makes sure, that one function call is only executed after the other has fineshed (not concorrent)
return doClick(times -1)
})
} else {
return browser
}
}
doClick(recursions)
}
}
In your case you would call the function 'select a competition' with 10 as the parameter "recursions".
Note that I have changed "randomCompetitionReference" to be a function, so this generates a different value every time it is called. Else it would get one random value when its defined, and would reuse that same value for every click().

Using _.each and $q promise to iterate widgets

I have a pretty straight-forward problem where I'm :
Iterating through a series of dashboard "widgets" using _.each().
Calling a function to refresh the current widget, and returning a $q promise.
Now, my issue is that I would like each iteration to WAIT prior to continuing to the next iteration.
My first version was this, until I realized that I need to wait for updateWidget() to complete:
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
updateWidget(wid, parentWidgetData);
}
});
My second version is this one, which returns a promise. But of course, I still have the problem where the iteration continues without waiting :
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
updateWidget(wid, parentWidgetData).then(function(data){
var i = 1;
});
}
});
and the called function which returns a deferred.promise object (then makes a service call for widget data) :
function updateWidget(widget, parWidData) {
var deferred = $q.defer();
// SAVE THIS WIDGET TO BE REFRESHED FOR THE then() SECTION BELOW
$rootScope.refreshingWidget = widget;
// .. SOME OTHER VAR INITIALIZATION HERE...
var url = gadgetDataService.prepareAggregationRequest(cubeVectors, aggrFunc, typeName, orderBy, numOrderBy, top, filterExpr, having, drillDown);
return gadgetDataService.sendAggGetRequest(url).then(function (data) {
var data = data.data[0];
var widget = {};
if ($rootScope.refreshingWidget) {
widget = $rootScope.refreshingWidget;
}
// BUILD KENDO CHART OPTIONS
var chartOptions = chartsOptionsService.buildKendoChartOptions(data, widget);
// create neOptions object, then use jquery extend()
var newOptions = {};
$.extend(newOptions, widget.dataModelOptions, chartOptions);
widget.dataModelOptions = newOptions;
deferred.resolve(data);
});
return deferred.promise;
}
I would appreciate your ideas on how to "pause" on each iteration, and continue once the called function has completed.
thank you,
Bob
******* UPDATED ************
My latest version of the iteration code include $q.all() as follows :
// CREATE ARRAY OF PROMISES !!
var promises = [];
_.each(widgets, function (wid) {
if (wid.dataModelOptions.linkedParentWidget) {
promises.push(updateWidget(wid, parentWidgetData));
}
});
$q.all(promises)
.then(function () {
$timeout(function () {
// without a brief timeout, not all Kendo charts will properly refresh.
$rootScope.$broadcast('childWidgetsRefreshed');
}, 100);
});
By chaining promises
The easiest is the following:
var queue = $q.when();
_.each(widgets, function (wid) {
queue = queue.then(function() {
if (wid.dataModelOptions.linkedParentWidget) {
return updateWidget(wid, parentWidgetData);
}
});
});
queue.then(function() {
// all completed sequentially
});
Note: at the end, queue will resolve with the return value of the last iteration
If you write a lot of async functions like this, it might be useful to wrap it into a utility function:
function eachAsync(collection, cbAsync) {
var queue = $q.when();
_.each(collection, function(item, index) {
queue = queue.then(function() {
return cbAsync(item, index);
});
});
return queue;
}
// ...
eachAsync(widgets, function(wid) {
if (wid.dataModelOptions.linkedParentWidget) {
return updateWidget(wid, parentWidgetData);
}
}).then(function() {
// all widgets updated sequentially
// still resolved with the last iteration
});
These functions build a chain of promises in the "preprocessing" phase, so your callback is invoked sequentially. There are other ways to do it, some of them are more efficient and use less memory, but this solution is the simplest.
By delayed iteration
This method will hide the return value even of the last iteration, and will not build the full promise chain beforehands. The drawback is that, it can be only used on array like objects.
function eachAsync(array, cbAsync) {
var index = 0;
function next() {
var current = index++;
if (current < array.length) {
return $q.when(cbAsync(array[current], current), next);
}
// else return undefined
}
// This will delay the first iteration as well, and will transform
// thrown synchronous errors of the first iteration to rejection.
return $q.when(null, next);
}
This will iterate over any iterable:
function eachAsync(iterable, cbAsync) {
var iterator = iterable[Symbol.iterator]();
function next() {
var iteration = iterator.next();
if (!iteration.done) {
// we do not know the index!
return $q.when(cbAsync(iteration.value), next);
} else {
// the .value of the last iteration treated as final
// return value
return iteration.value;
}
}
// This will delay the first iteration as well, and will transform
// thrown synchronous errors of the first iteration to rejection.
return $q.when(null, next);
}
Keep in mind that these methods will behave differently when the collection changes during iteration. The promise chaining methods basically build a snapshot of the collection at the moment it starts iteration (the individual values are stored in the closures of the chained callback functions), while the latter does not.
Instead of trying to resolve each promise in your _.each(), I would build out an array of promises in your _.each to get an array like:
promises = [gadgetDataService.sendAggGetRequest(url1), gadgetDataService.sendAggGetRequest(url2)....]
Then resolve them all at once, iterate through the results and set your models:
$q.all(promises).then(function(results){ // iterate through results here })

How to execute promises "sync" and not in async way

I calling getBubblesUserAccess that returns json objects that are orderd in a special way. This results i wanna run a foreach and get other messages but there i wanna return them in "order". I know that it will run these async but there must be a way that i can force it to "sequential" execution. (above code is my last attempt to add a defer...)
Example
pseudo code - get my groups
{
"id":"016cd1fc-89a3-4e4a-9e6e-a102df1b03d9",
"parent":"53750396-7d26-41f3-913d-1b93276b9e09",
"name":"XX",
"createdBy":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"hasWriteAccess":true,
"hasCreateAccess":false,
"hasDeleteAccess":false,
"hasAdminAccess":false,
"settingsBubbleId":"00000000-0000-0000-0000-000000000000"
},
{
"id":"016cd1fc-89a3-4e4a-9e6e-a102df1b03d9",
"parent":"53750396-7d26-41f3-913d-1b93276b9e09",
"name":"XX",
"createdBy":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"hasWriteAccess":true,
"hasCreateAccess":false,
"hasDeleteAccess":false,
"hasAdminAccess":false,
"settingsBubbleId":"00000000-0000-0000-0000-000000000000"
}
From this result i wanna iterate over those parent id strings and call another service that respond with this.
pseudo code
for each group above call another service with parent id and get result. This result will be added to a new JSON object.
"messages":[
{
"id":"f1d1aeda-d4e2-4563-85d5-d954c335b31c",
"text":"asd",
"sent":"2015-09-10T22:31:09.897+00:00",
"sender":"6b9e404b-ef37-4d07-9267-3e7b2579003b",
"senderName":"XXX XXXX"
},
{
"id":"a7ac0432-e945-440e-91ce-185170cbf3de",
"text":"asd",
"sent":"2015-09-10T22:28:24.383+00:00",
"sender":"c9c63080-2c5b-4e8e-a093-2cfcd628a9d0",
"senderName":"ZZZZZ ZZZZ"
},
My problem is that my second foreach are running async (as it should) and i want it to resolve back in same order as first json object...
My code::
var loadBubblesAccess = function () {
if (vm.running && angular.isDefined(vm.running)) { return; }
vm.running = true;
vm.bubblesWithMessages = null;
return BubbleFactory.getBubblesUserAccess().then(function (bubblesAccessTo) {
return bubblesAccessTo;
});
},
loadSubBubbles = function (bubblesAccessTo) {
/**
* Result from chain method with all bubbles user has access to.
*/
var promiseArray = [];
//var promiseArrayError = [];
var i = 0;
/**
* Creates a defer object so that we will not resolve before for each loop has been gone thru.. async problems.
*/
var deferred = $q.defer();
angular.forEach(bubblesAccessTo, function (bubble) {
$log.error(JSON.stringify(bubblesAccessTo));
/**
* Get 20 because default thats default and cache and e-tags are done to that number..
*/
BubbleFactory.getBubbleMessages(bubble.id, 0, 20, false).then(function (data) {
i++;
if (data.messages.length > 0) {
promiseArray.push({ bubbleSortOrder: i, bubbleId: bubble.parent, bubbleName: bubble.name, bubbleMessagesId: bubble.id, bubbleMessages: smartTrim(data.messages[0].text, 400, ' ', ' ...'), bubbleMessagesSent: data.messages[0].sent });
}
else {
// console.log("YYYY::: " + bubble.parent);
promiseArray.push({ bubbleSortOrder:i, bubbleId: bubble.parent, bubbleName: bubble.name, bubbleMessagesId: bubble.id, bubbleMessages: 'Inget meddelande än..', bubbleMessagesSent: '' });
}
});
/**
* Check if we have gone thru all bubbles - when finished we resolve defer object.
*/
if(i===bubblesAccessTo.length)
{
deferred.resolve(promiseArray);
}
});
//$log.debug.log(promiseArray);
vm.bubblesWithMessages = promiseArray;
promiseArray.length = 0;
vm.running = false;
};
loadBubblesAccess().then(loadSubBubbles);
The $q service in AngularJS is described as "lightweight" because it only implements the functions 90% of people need. That keeps its code size small - at the expense of not being able to address requests like yours very easily.
If you have the option, try an alternative such as bluebird. Bluebird provides a reduce() function that can execute an array of promises serially, and return their results in the order they were requested. It makes this task straightforward because your result array will match your data array and you can match up the results very easily.
If you do NOT have that option, there is a standard (if not-exactly-simple) technique with promises where you build an array of the elements you want to promise, then call the processing function (that returns a Promise) on the first value (popped from the array). In the .finally() handler, call the processing function recursively with the next value until it is empty (or an error occurs).
Pseudo-code for this:
var valuesToProcess = [1, 2, 3],
results = [];
function processValue(val) {
myProcessingFunction(val).then(function(result) {
results.push(result);
}).catch(function(e) {
console.log('FAIL!', e);
}).finally(function() {
if (valuesToProcess.length > 0) {
processValue(valuesToProcess.shift());
} else {
// All done - do something with results here
}
});
}
// Note: No error checking done, assumes we have work to do...
processValue(valuesToProcess.shift());
You'll need to adapt this to your use-case but it's a simple technique that guarantees serial operation and result-handling.

Cannot get iteration count of for loop cycle inside of the service result -> then

i have following method:
var i;
$scope.playAllSelectedSounds = function() {
try {
for( i; i < $scope.selectedSounds.length; i++) {
var fileName = $scope.selectedSounds[i].file;
var volume = $scope.selectedSounds[i].defaultVolume;
var filePath = "sounds/" +fileName+".mp3";
console.log(fileName);
MediaSrv.loadMedia(filePath).then(function(media){
console.log(media);
// !!!!!!!!!!! HERE I CANNOT GET value of the i VARIABLE
$scope.selectedSounds[i].state = 1;
// !!!!!!!!!!! HERE I CANNOT GET value of the i VARIABLE
$scope.selectedSounds[i].mediaInstance = media;
media.play();
media.setVolume(volume);
});
}
} catch(e) {
alert(JSON.stringify(e));
console.log(e);
$scope.showAlert("Error", "Error during the playing item");
}
};
Problem is that inside of the service:
MediaSrv.loadMedia(filePath).then(function(media){
I cannot get number o for cycle loop which i need to set in:
$scope.selectedSounds[i].state = 1;
Variable i is global i still cannot reach them. How can i solve it please?
It is not because i is not accessible, it is because i has run out of its limit because loadMedia is async and the value of i within the callback would become $scope.selectedSounds.length, since the for loop would have run out before the callback is invoked.
You could resolve this by using a closure variable representing the current item: You could just make use angular.forEach itself, and you don't event need to worry about accessing the right index. Instead just modify the object itself which is available as 1st argument of forEach evaluator function.
angular.forEach($scope.selectedSounds, function loadMedia(selectedSound, idx){
var fileName = selectedSound.file;
var volume = selectedSound.defaultVolume;
var filePath = "sounds/" +fileName+".mp3";
MediaSrv.loadMedia(filePath).then(function(media){
selectedSound.state = 1;
selectedSound.mediaInstance = media;
media.play();
media.setVolume(volume);
});
});
Also you forgot to initialize i in your case, which will cause your loop to not run at all.
Does it work if you create a local variable var that=i; just before your call to the promise, and then try to get "that" inside the promise return?
Otherwise try this :
for (var i in superarray){
(function(j) {
MyService.get(superarray[j].externalID).then(function(r) {
console.debug(j);
});
})(i);
}

Testing Angular Filter That Returns An Array with Jasmine

So, I'm having issues testing an angular filter that takes an array that has previously been sorted by a group property. It uses a flag property to indicate that the item is the first observation of that group, and then false for subsequent observations.
I'm doing this to have a category header in the UI with an ng-repeat directive.
When I test the filter, the output does not return the array with the flags unless I create new objects for the return array. This is a problem, because it causes an infinite loop when running in a webpage. The code works in the webpage when it just adds a flag property to the input object.
Is there some additional step I should be taking to simulate how angular handles filters so that it outputs the proper array?
This is what my test looks like right now.
describe('IsDifferentGroup', function() {
var list, itemOne, itemTwo, itemThree;
beforeEach(module("App.Filters"));
beforeEach(function () {
list = [];
itemOne = new ListItem();
itemTwo = new ListItem();
itemThree = new ListItem();
itemOne.group = "A";
itemTwo.group = "B";
itemThree.group = "C";
list.push(itemOne);
list.push(itemOne);
list.push(itemOne);
list.push(itemOne);
list.push(itemTwo);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
list.push(itemThree);
});
it('should flag the items true that appear first on the list.', (inject(function (isDifferentGroupFilter) {
expect(list.length).toBe(10);
var result = isDifferentGroupFilter(list);
expect(result[0].isDifferentGroup).toBeTruthy();
expect(result[1].isDifferentGroup).toBeFalsy();
expect(result[4].isDifferentGroup).toBeTruthy();
expect(result[5].isDifferentGroup).toBeTruthy();
expect(result[6].isDifferentGroup).toBeFalsy();
expect(result[9].isDifferentGroup).toBeFalsy();
})));
});
And here is something like the code with the filter:
var IsDifferentGroup = (function () {
function IsDifferentGroup() {
return (function (list) {
var arrayToReturn = [];
var lastGroup = null;
for (var i = 0; i < list.length; i++) {
if (list[i].group != lastGroup) {
list[i].isDifferentGroup = true;
lastAisle = list[i].group;
} else {
list[i].isDifferentGroup = false;
}
arrayToReturn.push(list[i]);
}
return arrayToReturn;
});
}
return IsDifferentGroup;
})();
Thanks!
I figured out my issue.
When I was passing the items into the list, I just pushed a pointer to an item multiple times. I was not passing in unique objects so the flag was being overridden by the following flag in the array(I think). So, I just newed up 10 unique objects using a loop, pushed them into the array and ran it through the filter. And it worked.
I'm not entirely sure my analysis is correct about the override, because itemTwo was not being flagged as unique when it was the only itemTwo in the array. But the test is working as I would expect now so I'm going to stop investigating the issue.

Resources