Saving To MongoDB In A Loop - javascript

i am having trouble saving a new record to mongoDB. i am pretty sure there is something i am using in my code that i don't fully understand and i was hoping someone might be able to help.
i am trying to save a new record to mongoDB for each of the cats. this code is for node.js
for(var x = 0; x < (cats.length - 1); x++){
if (!blocked){
console.log("x = "+x);
var memberMessage = new Message();
memberMessage.message = message.message;
memberMessage.recipient = room[x].userId;
memberMessage.save(function(err){
if (err) console.log(err);
console.log(memberMessage + " saved for "+cats[x].name);
});
}
});
}
i log the value of "cats" before the loop and i do get all the names i expect so i would think that looping through the array it would store a new record for each loop.
what seems to happen is that when i look ta the the database, it seems to have only saved for the last record for every loop cycle. i don't know how/why it would be doing that.
any help on this is appreciated because I'm new to node.js and mongoDB.
thanks.

That's because the save is actually a I/O operation which is Async. Now, the for loop is actually sync.
Think of it this way: your JS engine serially executes each line it sees. Assume these lines are kept one-after-another on a stack. When it comes to the save, it keeps it aside on a different stack (as it is an I/O operation, and thus would take time) and goes ahead with the rest of the loop. It so turns out that the engine would only check this new stack after it has completed every line on the older one. Therefore, the value of the variable cats will be the last item in the array. Thus, only the last value is saved.
To fight this tragedy, you can use mutiple methods:
Closures - Read More
You can make closure like so: cats.forEach()
Promises - Read More. There is a sweet library which promisifies the mongo driver to make it easier to work with.
Generators, etc. - Read More. Not ready for primetime yet.
Note about #2 - I'm not a contributor of the project, but do work with the author. I've been using the library for well over an year now, and it's fast and awesome!

You can use a batch create feature from mongoose:
var messages = [];
for(var x = 0; x < (cats.length - 1); x++) {
if (!blocked) {
var message = new Message();
message.message = message.message;
message.recipient = room[x].userId;
messages.push(message);
}
}
Message.create(messages, function (err) {
if (err) // ...
});

Related

How to make the client-side code wait for the full execution of google.script.run?

I've got a Google Apps Script WebApp that relies on an array of objects that are generated from a Google Spreadsheet. The app uses jquery and miniSearch to provide user functionality.
Currently, I run the server-side function with a success handler at the beginning of the HTML tag and update a "global" variable with the array of objects declared before it.
Index.html:
<script>
let data
google.scripts.run
.withSuccessHandler(payload=>{
data = payload}).getLinks() //[{link:body}, {link1:body1}]
setTimeout(()=>{
const documents = data
miniSearch = new miniSearch(...)
miniSearch.addAll(documents)}, 2500)
...
</script>
Code.gs
function getLinks(){
.
.
.
let values = sheet.getRange(1, 1, lastRow, lastCol)
for (let row = 0; row < lastRow; row++) {
let entry = new Data(row + 1, values[row][0], values[row][1], values[row][2], values[row][3], values[row][4], values[row][5], values[row][6])
allTitles.push(entry)
}
return allTitles
}
I simulate waiting for the google.scripts.run to finish by setting a setTimeout of 2500ms on the miniSearch indexing execution (which relies on the aforementioned array) and most of the time it works.
The problem is, that before the app is ran for the first time in a given period, the contents are not cached and the execution takes longer than the setTimeout, thus, as expected, the searching function bugs out since it's got no data to run on.
My question is: How to make the code wait and confirm that google.scripts.run has returned the needed data?
I have tried doing it with regular promises or async await functions, but to my understanding, google runs its server functions asynchronously and there's no way to tell (in code, that is) if it's finished, I've tried running it as $(function(){google.script.run..}) as to try to load the contents as soon as the DOM is loaded, but to no avail..
The only way to make sure it finishes is to do this. If its unresponsive then the problem lies in getLinks, Data, or whatever miniSearch is.
<script>
const documents = null;
google.scripts.run.withSuccessHandler( function(payload) {
documents = payload;
miniSearch = new miniSearch(...);
miniSearch.addAll(documents);
}.getLinks(); //[{link:body}, {link1:body1}]
...
</script>

implementing timer in javascript using web workers

I am trying to implement a timer in javascript using web workers. I wrote the following code. But it's not working. Can someone point out why it's not working? I don't have much experience with javascript. So, it would be great if someone explains the reason in great detail.
Here's what I'm trying to do:
First creating a sharedArrayBuffer and a worker thread. And creating another array, on which, I will do some work and want to count the time. Then sending the sharedArrayBuffer to worker thread which increments the first value in the array in a for loop. Finally, I am reading that value in the main.js and I'm getting 0 every time.
main.js
var buffer = new SharedArrayBuffer(1024);
var i;
var uint32 = new Uint32Array(buffer);
var myWorker = new Worker('worker.js');
var array = new Uint32Array(8);
array[0] = 0;
console.log(Atomics.load(uint32,0),array[0]);
myWorker.postMessage(buffer);
for(i=0;i<300000000;i++) {
array[0] += i;
}
console.log(i,Atomics.load(uint32,0),array[0]);
worker.js
onmessage = function(buffer) {
console.log('from worker');
var uint32 = new Uint32Array(buffer.data);
for(i=0; ;i++) {
uint32[0] += 1;
};
}
You should not be using code like this to try and determine how long code takes to run. It's non-sensical because incrementing the count in an array is not tied to time or any unit of measurement. Instead, there are APIs which can be used to evaluate performance, such as console.time():
onmessage = function(buffer) {
console.time('TimeSpentInWorker');
// Your code...
console.timeEnd('TimeSpentInWorker');
};
You could also compare the difference between calling Date.now() twice or look into the Performance API.

Zapier: Task timed out after 1.00 seconds

I am using the Zapier Code application in the javascript language, I am making a request in an api but in almost all attempts at the time of executing the script, I get the error message: "We had trouble sending your test through. Please try again. Error:
2018-03-09T14:32:54.748Z c0958e0a-23a6-11e8-9be1-a515bc24f853 Task timed out after 1.00 seconds". Sometimes script execution happens successfully, but most of the time it gives this error.
The calling code in the api I'm using is this:
var promises = [];
var retornoDaChamada;
promises.push(fetch(urls));
Promise.all(promises).then(function(res){
var blobPromises = [];
for (var i = res.length - 1; i >= 0; i--) {
blobPromises.push(res[i].text());
}
return Promise.all(blobPromises);
}).then(function(body){
retornoDaChamada=JSON.parse(body);
var titulosDaApi = [retornoDaChamada.length];
var duracao = [retornoDaChamada.length];
var ids = [retornoDaChamada.length];
for(var i=0; i<retornoDaChamada.length; i++){
titulosDaApi[i]=retornoDaChamada[i].title;
duracao[i]=milissegundosParaHorasMinutosSegundos(retornoDaChamada[i].files[0].fileInfo.duration);
ids[i]=retornoDaChamada[i].id;
}
var output = {titulosDaApi, duracao, ids};
callback(null, output);
}).catch(callback);
I read the documentation of the application Code and I kind of understood that free user only has the time of up to 1 second for calls in Api, is there any way I can get around this problem even though I am a free user?
David here, from the Zapier Platform team.
It's a little tough to understand the context of your code, but it looks like you're doing multiple HTTP requests. Due to their nature, they're a very slow operation. If you're doing more than 1 (maybe two if the external resource responds really quickly), you're unlikely to be able to fit everything into 1 second.
Sorry for the bad news!

Ussing setTimeout inside a loop to not allow blocking

Some lines of code to give you the idea what I'm trying to ask.
Code starts with
var webSocketsServerPort = 8002;
var webSocketServer = require('websocket').server;
var conns = [];
I use the array conns to push the users after each successful connection. I put there additional (their ID) information so I can identify the user.
And when I need to send a specific information to a user I call the following function.
function sendMessage(userID, message){
for(var i = 0, len = conns.length; i < len; ++i){
if(conns[i].customData.ID == userID){
conns[i].sendUTF(message);
}
}
}
My question is:
Is it a better idea if replace conns[i].sendUTF(message); with setTimeout(function(){conns[i].sendUTF(message)},1) so that in case there are 5000 connected users sendUTF(msg) will not be able to block the loop and in the best case all the messages will be sent at the same time.
If you change your design to order everything by an id instead of an Array of objects, there is no reason to have to loop to find all of the user's connection. You would only need to loop through the multiple connections for each user.
var connections = {};
function addConnection (userId, conn) {
if (!connections[userId]) {
connections[userId] = [];
}
connections[userId].push(conn);
}
var getUserConnections (userId) {
return connections[userId];
}
That wouldn't help in the way you are thinking. If it's not going to "block" at that time, it will "block" in 1 ms.
Doing setTimeout that way will only delay the execution, but not the queueing. JS will still blockingly run your for loop to get all 5000 items into the waiting queue before clearing the stack for the other things.
What you need is to give way each iteration. Since you're on NodeJS, you can use process.nextTick() to schedule the next iteration. Here's a quick example.
var i = 0;
var length = cons.length;
function foo(){
// if not yet the limit, schedule the next
if(i++ < length) process.nextTick(foo);
// Run as usual
if(conns[i].customData.ID == userID) conns[i].sendUTF(message);
}

How to get the value of SELECT COUNT(*)?

I've literally been trying all day to make Firefox to obey my will...
I want :
int c = SELECT COUNT(*) FROM ...
I've tried executeAsync({...});, but I believe it's the wrong paradigm, as I want the result immediately. (And mozIStoragePendingStatement results in errors)
var count = 0;
var conn = Services.storage.openDatabase(dbfile); // Will also create the file if it does not exist
let statement = conn.createStatement("SELECT COUNT(*) FROM edges LIMIT 42;");
console.log("columns: " + statement.columnCount); // prints "1";
console.log("col name:" + statement.getColumnName(0)); // is "COUNT(*)"
while (statement.executeStep())
count = statement.row.getResultByIndex(0); // "illegal value"
count = statement.row.getString(0); // "illegal value", too
count = statement.row.COUNT(*); // hahaha. still not working
count = statement.row[0]; // hahaha. "undefinded"
count = statement.row[1]; // hahaha. "undefinded"
}
statement.reset();
It basically works but I dont get the value. What's wrong with all the statements (those within the loop).
Thanks for any hints...
I've tried executeAsync({...});, but I believe it's the wrong paradigm, as I want the result immediately.
You shouldn't want that, the Storage API is asynchronous for a reason. Synchronous access to databases can cause a random delay (e.g. if the hard drive is busy). And since your code executes on the main thread (the same thread that services the user interface) the entire user interface would hang while your code is waiting for the database to respond. The Mozilla devs tried synchronous database access in Firefox 3 and quickly noticed that it degrades user experience - hence the asynchronous API, the database processing happens on a background thread without blocking anything.
You should change your code to work asynchronously. Something like this should do for example:
Components.utils.import("resource://gre/modules/Services.jsm");
var conn = Services.storage.openDatabase(dbfile);
if (conn.schemaVersion < 1)
{
conn.createTable("edges", "s INTEGER, t INTEGER");
conn.schemaVersion = 1;
}
var statement = conn.createStatement("SELECT COUNT(*) FROM edges");
statement.executeAsync({
handleResult: function(resultSet)
{
var row = resultSet.getNextRow();
var count = row.getResultByIndex(0);
processResult(count);
},
handleError: function(error) {},
handleCompletion: function(reason) {}
});
// Close connection once the pending operations are completed
conn.asyncClose();
See also: mozIStorageResultSet, mozIStorageRow.
Try aliasing count(*) as total, then fetch that

Categories

Resources