Node acting synchronously, waiting for the loop to finish - node.js

I have encounted a few issues in the project I am doing since node is async, but now I have a for loop and I do something after the loop ends or breaks, and it seems to be working synchronously and I want to know why.
I have this code:
var newItem = true;
for (var i = 0; i < cart.length; i++) {
if (cart[i].title == slug) {
cart[i].qty++;
newItem = false;
break;
}
}
if (newItem) {
cart.push({
title: slug
});
}
And all this works as expected! the newItem variable always has the correct value.
How come newitem is not always true?
I never have more than 3 items in the cart array, is this why? Would it behave differently if I had a 1000 items?

The code you have provide is running synchronously, if you want to test whether it works in an asynchronous condition try running this code
var newItem = true;
for (var i = 0; i < cart.length; i++) {
setTimeout(function(){
if (cart[i].title == slug) {
cart[i].qty++;
newItem = false;
break;
}
}, 1000);
}
if (newItem) {
cart.push({
title: slug
}
}

Related

In firebase cloud function the child is self-deleting

When I add a transaction in the function below, the child of the "guncelLig" is automatically deleted. When I remove the second transaction there is no problem, it works very well.
this code work correct :
for (let i = 0; i < users.length; i++) {
const userID = users[i].id;
database.ref().child("users").child("profilData").child(userID).child("guncelLig").transaction(function (value) {
if (value > 1) {
database.ref().child("users").child("profilData").child(userID).update({
"lastUpdate": toSwiftDate(),
});
return value - 1;
}
return value;
});
}
but when I add the new part to the code below, "guncelLig" is completely deleted from the firebase realtime databse:
for (let i = 0; i < users.length; i++) {
const userID = users[i].id;
database.ref().child("users").child("profilData").child(userID).child("guncelLig").transaction(function (value) {
if (value > 1) {
database.ref().child("users").child("profilData").child(userID).update({
"lastUpdate": toSwiftDate(),
});
/// NEW CODE ERROR SOURCHE
database.ref().child("users").child("profilData").child(userID).child("EnYukselLig").transaction(function (EnYukselLig) {
if (EnYukselLig > value - 1) {
return value - 1;
}
return EnYukselLig;
});
return value - 1; // arttır
}
return value; /// ALWAYS RETURN NULL
});
}
Where am I doing wrong? please help me
You should write your code as async/await. With await you can wait for the nested transaction to finish.
for (let i = 0; i < users.length; i++) {
const userID = users[i].id;
database
.ref()
.child("users")
.child("profilData")
.child(userID)
.child("guncelLig")
.transaction(async (value) => {
if (value > 1) {
await database
.ref()
.child("users")
.child("profilData")
.child(userID)
.update({
lastUpdate: toSwiftDate(),
});
/// NEW CODE ERROR SOURCHE
await database
.ref()
.child("users")
.child("profilData")
.child(userID)
.child("EnYukselLig")
.transaction(function (EnYukselLig) {
if (EnYukselLig > value - 1) {
return value - 1;
}
return EnYukselLig;
});
return value - 1; // arttır
}
// No handler if the value is NULL
return value; /// ALWAYS RETURN NULL
});
}
You also don't handle the case if value is already null.
It is not recommmended to nest transactions like that. Transacions by nature can run multiple times and there is no guaranty that they finish as expected depending on the amount of updates that happen on the same location.

Recursively get documents in Azure Cosmos Stored Procedure

I'm trying to generate a content tree for a simple wiki. Each page has a Children property that stores the id of other wiki pages. I'm trying to write a SPROC that gets all of the documents, then iterate over each page's Children property and replace each item with an actual wiki page document. I'm able to get the first set of documents, but the wikiChildQuery returns undefined and I'm not quite sure why.
I've been able to get a document with the query alone but for some reason, it doesn't work within the SPROC. Is there something I'm missing here?
function GetWikiMetadata(prefix) {
var context = getContext();
var collection = context.getCollection();
var metadataQuery = 'SELECT {\"ExternalId\": p.ExternalId, \"Title\": p.Title, \"Slug\": p.Slug, \"Children\": p.Children} FROM Pages p'
var metadata = collection.queryDocuments(collection.getSelfLink(), metadataQuery, {}, function (err, documents, options) {
if (err) throw new Error('Error: ', + err.message);
if (!documents || !documents.length) {
throw new Error('Unable to find any documents');
} else {
var response = getContext().getResponse();
for (var i = 0; i < documents.length; i++) {
var children = documents[i]['$1'].Children;
if (children.length) {
for (var j = 0; j < children.length; j++) {
var child = children[j];
children[j] = GetWikiChildren(child);
}
}
}
response.setBody(documents);
}
});
if (!metadata) throw new Error('Unable to get metadata from the server');
function GetWikiChildren(child) {
var wikiChildQuery = metadataQuery + ' WHERE p.ExternalId = \"' + child + '\"';
var wikiChild = collection.queryDocuments(collection.getSelfLink(), wikiChildQuery, {}, function(err, document, options) {
if (err) throw new Error('Error: ', + err.message);
if (!document) {
throw new Error('Unable to find child Wiki');
} else {
var children = document.Children;
if (children) {
for (var k = 0; k < children.length; k++) {
var child = children[k];
children[k] = GetWikiChildren(child);
}
} else {
return document;
}
}
if (!wikChild) throw new Error('Unable to get child Wiki details');
});
}
}
1.I'm able to get the first set of documents, but the wikiChildQuery
returns undefined and I'm not quite sure why.
Firstly, here should be corrected. In the first loop, you get children array with documents[i]['$1'].Children, however , in the GetWikiChildren function you want to get children array with document.Children? Surely,it is undefined. You need to use var children = document[0]['$1'].Children;
2.It seems that you missed the replaceDocument method.
You could refer to the snippet code of your metaDataQuery function:
for (var i = 0; i < documents.length; i++) {
var children = documents[i]['$1'].Children;
if (children.length) {
for (var j = 0; j < children.length; j++) {
var child = children[j];
children[j] = GetWikiChildren(child);
}
}
documents[i]['$1'].Children = children;
collection.replaceDocument(doc._self,doc,function(err) {
if (err) throw err;
});
}
3.Partial update is not supported by Cosmos db SQL Api so far but it is hitting the road.
So, if your sql doesn't cover your whole columns,it can't be done for your replace purpose.(feedback) It is important to note that the columns which is not mentioned in the replace object would be devoured while using replaceDocument method.

Custom module data returning successfully but not displaying

I have a custom module in file help.js, the function name there is getfriends(req,res,next,user). The user is whose friends I want to get.
for (var i = 0; i < users.length; i++) {
for (var j = 0; j < docs.length; j++) {
if (docs[j].user1 == users[i].username) {
if (docs[j].user1 != req.body.user) {
friends.push(users[i]);
}
} else if (docs[j].user2 == users[i].username) {
if (docs[j].user2 != req.body.user) {
friends.push(users[i]);
}
}
}
if (i == users.length-1) {
console.log("friends",friends); //it displays my desired result and so I think the return is successfull
return(friends);
}
}
Now where I receive the data, I do this and data is not being displayed.
console.log(Help.getfriends(req,res,next,req.session.user));
I have tried doing :-
somevar = Help.getfriends(req,res,next,req.session.user);
console.log(somevar);
The module is being called, it is displaying perfect result. Please guide me how to get the data properly from the custom module.
Also, above I have done,
var Help = require('./help');
Your function is asynchronous.
When you console.log(...) <== there is no result yet.
So i'ts expected that you console.log undefined.
More info about nodejs asyncronous nature.

How to fix jslint warning Don't make functions within a loop

Getting this warning on the following code:
workflow.removeZSets = function(fn) {
var processed = 0;
for (var c = 1; c < 10; c++) {
workflow.removeZSet(c, function() {
processed++;
if (processed === 9) {
return fn(null, "finished removing");
}
});
}
}
workflow.removeZSet = function(precision, fn) {
rc.zrem("userloc:" + precision, function() {
return fn(null, 'done');
});
});
}
Does anyone have a suggestion how to accomplish this without triggering the warning?
I have some ideas like using the async library to run them all in parallel but this is a fairly common thing I do throughout this code base so interested in feedback on the best way.
The error is because you have define a function within your for loop.
You could try something like this, defining the function outside of the loop:
workflow.removeZSets = function(fn) {
var processed = 0;
function removeZ(c) {
workflow.removeZSet(c, function(err) {
processed++;
if (processed === 9) {
return fn(null, "finished removing");
}
});
}
for (var c = 1; c < 10; c++) {
removeZ(c);
}
}
Using a library like async to do the looping would help clean up your code, it would allow you to avoid checking if all the items have processed (processed ===9) because it is handled by async.

How to get an object that was changed in angularjs?

I use this function to watch an array of objects for changes:
$scope.$watch('Data', function (newVal) { /*...*/ }, true);
How can I get an object in which property has been changed so that I can push it in an array?
For example:
var myApp = angular.module("myApp", []);
myApp.factory("Data", function(){
var Data = [{id:1, property: "Random"}, {id:2, property: "Random again"}];
return Data;
});
var myBigArray = [];
function tableCtrl($scope, Data){
$scope.TheData = Data;
$scope.$watch("TheData", function() {
//Here an object should be pushed
myBigArray.push(">>Object in which property has been changed <<<");
}, true);
}
I don't see a way currently in Angular to get the changed object... I suspect you might need to traverse the new array and try to find the differences with the old array...
Edit: Note that this solution turns out to be a bad practice as it is adding a lot of watchers, which is something you do not want because it has a performance penalty.
=======
I eventually came up with this solution:
items.query(function (result) {
_(result).each(function (item, i) {
$scope.items.push(item);
$scope.$watch('items[' + i + ']' , function(){
console.log(item); // This is the item that changed.
}, true);
});
});
There is still no option like this for $watch, but you can use jQuery plugin for that, http://archive.plugins.jquery.com/project/jquery-diff
I implemented undo/redo with AngularJS using $watch, mb this can help
//History Manager Factory
.factory('HistoryManager', function () {
return function(scope) {
this.container = Array();
this.index = -1;
this.lock = false;
//Insert new step into array of steps
this.pushDo = function() {
//we make sure that we have real changes by converting to json,
//and getting rid of all hash changes
if(this.container.length == 0 || (angular.toJson(scope.widgetSlider) != angular.toJson(this.container[this.index][0]))) {
//check if current change didn't came from "undo" change'
if(this.lock) {
return;
}
//Cutting array, from current index, because of new change added
if(this.index < this.container.length-1) {
this.container = this.container.slice(0, this.index+1);
}
var currentStepSlider = angular.copy(scope.widgetSlider);
var selectedWidgetIndex = scope.widgetSlider.widgets.indexOf(scope.widgetCurrent);
//Initialising index, because of new "Do" added
this.index = this.container.length;
this.container.push([currentStepSlider, selectedWidgetIndex]);
if (this.onDo) {
this.onDo();
}
}
}
//Upon undo returns previous do
this.undo = function() {
this.lock = true;
if(this.index>0){
this.index--;
scope.widgetSlider = angular.copy(this.container[this.index][0]);
var selectedWidgetIndex = this.container[this.index][1];
scope.widgetCurrent = scope.widgetSlider.widgets[selectedWidgetIndex];
}
this.lock = false;
}
//Upon redo returns next do
this.redo = function() {
if(this.index < this.container.length-1) {
this.index++;
scope.widgetSlider = angular.copy(this.container[this.index][0]);
var selectedWidgetIndex = this.container[this.index][1];
scope.widgetCurrent = scope.widgetSlider.widgets[selectedWidgetIndex];
}
}
}
})
;

Resources