Firebase Cloud Functions Change another node's data in forEach loop - javascript

I am trying to remove item from a node in for each loop on my cloud function. But it always looking only first item. I want to loop all items. Here is my codes:
snapshot.forEach(function(child) {
console.log('all items');
return thisAppDb.ref('userRemindMatches/'+child.key+'/'+compId).once('value').then(snapshot => {
if (snapshot.exists()) {
snapshot.ref.remove();
console.log('match removed from this user : '+child.key);
}
return thisAppDb.ref('user : '+child.key+' matches removed');
});
});
As you see here, in snapshot.foreach, I am calling another ref. Node then I want to remove item from that node. It is working only once. How can I loop this for each items? (In my opinion, it occurs because of return in loop)

change this:
return thisAppDb.ref('userRemindMatches/'+child.key+'/'+compId).once('value').then(snapshot => {
to this:
return thisAppDb.ref('userRemindMatches/'+child.key+'/'+compId).on('value',(snapshot)=> {

The documentation for forEach makes it clear how the return value from your passed function is interpreted:
function(non-null admin.database.DataSnapshot)
A function that will be called for each child DataSnapshot. The
callback can return true to cancel further enumeration.
You're returning a "truthy" value, which tells forEach to stop.

Related

React calling functions resunting in loop

I am trying to understand why this results in a loop :
<button onClick={this.moveRight()}>
but this does not:
<button onClick={this.moveRight}>
Also my my problem is to call a function from another function that is also resulting a loop, why ?
anyfunction(){
anotherfunction();
}
anotherfunction(){
if (this.state.something >1 ){
this.setState(PrevState => {
PrevState.something = PrevState.something -1
return {something = PrevState.something};
});
this.anyfunction()
}
}
Why the if does not break the loop ?
If this.moveRight() calls itself, then unless you put a condition to stop the recursion, it'll continue to do so. Also, this answer tells you how to assign an event handler correctly.
Also my problem is to call a function from another function. That is also resulting a loop, why?
this.setState is async, so if you want the value of this.state.something to be set before you call this.anyfunction(), then you'll need to do something like:
function anotherfunction() {
if (this.state.something > 1) {
this.setState(
PrevState => {
return { something: PrevState.something - 1 };
},
() => {
this.anyfunction();
}
);
}
}
which says that call this.anyFunction once you're done doing the state change that I asked for. More on that here.
The reason why
<button onClick={this.moveRight()}>
results in a loop is having the parenthesis calls the function, rather than setting onClick to the functions reference.
The second issue is also resulting in a loop because the function's both call each other, if this.state.something > 1 never evaluates to false.

How do I fix my Javascript function that does not work?

I am starting to learn Javascript and I do not understand the following:
I need to execute the method "pay" in order to pay all the different persons; so, I need to complete the function "salary". The function receives an array object; all those objects "know" how to execute the method "pay". Also, I want to store the result in the array "result".
I did this but it seems it's not working:
function salary($persons) {
$results= [];
$persons->pay();
return $results;
}
What am I missing? What's wrong with my function?
-> is not Javascript syntax.
To construct one array by performing an operation on each item of another array, use .map:
function salary(persons) {
return persons.map(person => person.pay());
}
function salary(persons) {
return persons.map(person => person.pay());
}
console.log(salary([
{ pay: () => 5 },
{ pay: () => 10 }
]));
Since this isn't PHP, there's no need to prefix variables with $ either.

if condition inside forEach's anonymous function does not work

Consider the code snippet below:
function isUniform(myArray) {
myArray.forEach(function(element) {
if (element !== myArray[0]) {
return false;
}
});
return true;
}
The intention is that the function should take an array as input and return true if all the elements in the array are the same and false otherwise.
eg:
isUniform([1,2,1,1]); // should return false
isUniform([1,1,1,1]); // should return true
However, the if condition:
if (element !== myArray[0])
never seem to be true in the case of isUniform([1,2,1,1]).
What is it that I am missing ?
So the return true isn't returning a value for the function isUniform, it's returning a value for the callback that you provided to the forEach method. forEach is really only used to create side effects. So forEach executes the callback on each element, sees that the callback returns false, but then doesn't have anything to do with that value, so it throws it out and moves on to the next element in the array. After iterating through the array, it moves on to the next line of code and returns true for the function.
One way that you might do this using forEach is to declare a variable that you initialize as true and manipulate that within the callback. This is necessary, as there's not a way to end the execution of a forEach loop early. So you might instead use:
function isUniform(myArray) {
var passing = true;
myArray.forEach(function(element) {
if (element !== myArray[0]) {
passing = false;
}
});
return passing;
}
Or you could use a for loop or a for-of loop, in which case the return statement would work as you had originally expected. You're probably already familiar with for loops. For-of loops were introduced in ES2015 (so they might not work on all JS engines). A for-of loop would look like this:
function isUniform(myArray) {
for (element of myArray) {
if (element !== myArray[0]) {
return false
}
}
return true
}
However, the best way to do this is probably using the built in array method every, which returns true if every element in the array passes the test provided in the callback. So you might test every element to see if they're equal to the 0th element in the array and thus equal to each other:
function isUniform(myArray) {
return myArray.every(function (currentElement,index,array) {
return currentElement === array[0]
})
}
That's short enough that you really don't even need to put it in its own function -- and your code will probably be more readable if you don't.
Docs:
Array.prototype.every: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
For-of loop: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of

Is map() in javascript synchronous?

Function is :
[1,2,3].map( function (item)
{
console.log(item);
//return 'something';
});
My expected behaviour is getting only 1 as output, unless i uncomment the
//return 'something'
But i really get
1
2
3
What am i doing wrong ?
UPDATE:
i am testing that with nodejs.
i really dont understand.
var async = require("async");
[1,2,3].map( function (item)
{
console.log(item);
//return 'something';
});
async.map([1,2,3], function (item,callback)
{
console.log(item);
//callback(null,true)
}, function (err,result)
{
console.log(result);
}
);
Both return the same
1
2
3
And i really would like to wait till i get a return or a callback till the next item is executed.
SOLVED
async.mapSeries([1,2,3], function (item,callback)
{
console.log(item);
//callback(null,true)
}, function (err,result)
{
console.log(result);
}
);
is the way to do it.
Yes, map is synchronous.
It's a higher order function, that takes a new function and applies it to the given array.
Some people think that because they give a function as a parameter to map then it 'should' act like an event callback function, but it really doesn't. The map function just applies the function parameter to the array and only after it finishes, it continues execution for the resulting code after the map block.
As to your 'expected behavior' - it just doesn't work like you think ;)
"The map() method creates a new array with the results of calling a provided function on every element in this array."
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
The callback is called for each item, your logic is executed and the return value is set as an item in the new array.

How do I wrap executions of asynchronous (callback-based) functions into a synchronous function in Javascript?

I'm trying to write a function in Javascript (with jQuery, if you want):
function fetchItem(itemId) { return /* ??? */; }
This function relies on a second, predefined and unmodifyable function that looks like this:
function load(callback) { /* ... */ }
This function is asynchronous. After calling it, it fetches n items via XHR, then when they have arrived, stores them in the DOM, then invokes the callback.
fetchItem uses a simple jQuery selector (irrelevant here) to check the DOM for the element with itemId and calls load if the item isn't there yet. Rinse and repeat.
My problem is that I want to wrap multiple asynchronous calls of load into my synchronous fetchItem function, which should return the DOM element with itemId after it has made enough load calls.
Pseudo code, if load was synchronous:
function fetchItem(itemId):
while not dom.contains(itemId):
load()
return dom.find(itemId)
My first attempts at doing this in Javascript, which probably display a lot of misconceptions about Javascript's closures and execution model: ;)
function fetchItem(itemId) {
var match = undefined;
function finder() {
match = $(...).get(0);
if(!match) {
load(finder);
}
}
finder();
return match;
}
Obviously, this fails because the return is executed before the first callback. Also, as you can see I had some problems getting match back out to fetchItem. Is it properly protected by the closure here? Would this work if fetchItem was executed multiple times in parallel, assuming that load supports this (and doesn't mix up the DOM)?
I'm probably missing a perfectly good pattern here, but I don't really know what to google for...
You need to make fetchItems async too and provide it a callback, something like this should probably work (warning untested!):
function fetchItems(itemIDS, callback, matches) {
if (!matches) { // init the result list
matches = [];
}
// fetch until we got'em all
if (itemIDS.length > 0) {
var id = itemIDS[0]; // get the first id in the queue
var match = $(id).get(0);
// not found, call load again
if (!match) {
load(function() {
fetchItems(itemIDS, callback, matches);
});
// found, update results and call fetchItems again to get the next one
} else {
matches.push(match); // push the current match to the results
itemIDS.shift(); // remove the current id form the queue
fetchItems(itemIDS, callback, matches);
}
// we have all items, call the callback and supply the matches
} else {
callback(matches);
}
}
fetchItems(['#foo', '#bar', '#test'], function(matches) {
console.log(matches);
})
I would simply gave your fetchItem function as a callback to load. Like this:
function fetchItem(itemId, callback):
if not dom.contains(itemId):
load(fetchItem)
else:
callback(dom.find(itemId))
callback() is a function that does rest of the job when necessary element appears in the DOM.
That is impossible. You cannot create synchronousness from asynchronousness. Why do not you add a callback to your fetchItem-function as well?
Seems like everybody agrees that I need to introduce my own callback, so here's my (so far final) working solution:
var MAX_FETCH_MORE = 3;
/*
* Searches for itemId, loading more items up to MAX_FETCH_MORE times if necessary. When
* the item has been found or the maximum reload count has been reached, the callback
* is invoked, which is passed the DOM object of the item wrapped in a jQuery object, or
* undefined.
*/
function executeWithItem(itemId, callback, fetchCycleCounter) {
// initialize fetchCycleCounter on first iteration
if(!fetchCycleCounter) fetchCycleCounter = 0;
console.debug('iteration ' + fetchCycleCounter + '/' + MAX_FETCH_MORE);
// try to find the item in the DOM
match = $('div[data-item-id="' + itemId + '"]').get(0);
if(match) {
// if it has been found, invoke the callback, then terminate
console.debug('found: ' + match);
callback($(match));
} else if(!match && fetchCycleCounter < MAX_FETCH_MORE) {
// if it has not been found, but we may still reload, call load() and pass it
// this function as the callback
console.debug('fetching more...');
load(function() {executeWithItem(itemId, callback, fetchCycleCounter+1);});
} else {
// give up after MAX_FETCH_MORE attempts, maybe the item is gone
console.debug('giving up search');
}
}
// example invocation
executeWithItem('itemA01', function(item) {
// do stuff with it
item.fadeOut(10000);
});
Thanks to everybody for encouraging me to introduce another callback, it hasn't turned out looking so bad. :)

Categories

Resources