I'm using google-spreadsheet library to access and insert data to Google Sheets. I was able to add multiple rows but each row is being added in no particular order, I guess due to the addRow() async behavior.
Example data is:
let myData = [{id: "1", fname: "John"}, {id: "2", fname: "Matt"}, {id: "3", fname: "Paul"}]
for (let i = 0; i < myData.length; i++) {
let row = myData[i];
// Async call to insert row.
doc.addRow(sheetIndex, row, (err) => {
if (err) throw err;
});
}
For-loop iterates and passes each row object to addRow() but it doesn't care what the order is when each row gets inserted to Googlesheets.
I've tried doing an async-await approach to wait for doc.addRow() before it iterates to next row object but I have no luck. Is there a way to achieve the insertion in the same order I pass it? Thanks in advance! :)
You couldn't await the function probably because it does't return Promise. So, lets promisify that function first.
function asyncAddRow(sheetIndex, row) {
return new Promise((res, rej) => {
doc.addRow(sheetIndex, row, (err) => {
if (err) rej(err);
else res(true);
});
})
}
Now, instead of calling doc.addRow you call our asyncAddRow. Since it returns a promise so you can await it now. Like this:
for (let i = 0; i < myData.length; i++) {
let row = myData[i];
await asyncAddRow(sheetIndex, row);
}
You should use the method append()
let values = [
[
// Cell values ...
],
// Additional rows ...
];
let resource = {
values,
};
this.sheetsService.spreadsheets.values.append({
spreadsheetId,
range,
valueInputOption,
resource,
}, (err, result) => {
if (err) {
// Handle error.
console.log(err);
} else {
console.log(`${result.updates.updatedCells} cells appended.`);
}
});
More info there
Related
need some help in correcting a loop closure in javascript.
Required flow: MasterGet function is run, data is fetched from a mysql table, for each record fetched a set of 'rules' is run.
Issue faced: if there are two records fetched, while 'rules' are being run for record 1, the record 2 'rules' also get triggered. Need to modify code such at record 2 is checked only after the 'rules' action is completed for record 1.
function MasterGet() {
var countCheckRule = 0;
connection.query(
'SELECT * FROM MASTER',
function selectCb(error, rows, fields) {
if (error) {
console.log('Log 045 - GetData Error: ' + error.message);
return;
}
for (var i = 0; i < rows.length; i++) {
console.log(+ rows[i].INDEX1);
var firstResult = rows[i];
rules.checRules(firstResult, myhttp, function (rules_res) {
firstResult.rules = rules_res;
})
}
});
countCheckRule++;
setTimeout(funcL, 4000);
};
Any guidance will help. Thanks
Added to the issue:
The rules.checrules coding is as below:
exports.checRules = function (A, myhttp, _callback) {
var objrules = { 'rule12': false };
function rule11() {
if (A.NQ > 0 && A.PSQ > 0) {
objrules.rule11 = true;
if (config.execute) {
modifyOrder('S', 'A.BQ', A.TS);
}
} else {
objrules.rule11 = false;
}
}
rule11();
_callback(objrules);
}
So in the loop for 1st record, it checks rule11, and if rule11 is true then it has to execute 'modifyOrder' with the given variables, after 'modifyOrder' is completed, then go back to the loop and check for the 2nd record. If Rule11 is false for the 1st record, then it should automatically go back to the loop and check for the 2nd record.
Currently with the given changes, 2nd record check gets triggered before 'modifyOrder' is complete. Maybe the issue is that the code does not wait for the callback from 'modifyOrder'? Is that the issue? How can I make the code wait till 'modifyOrder' is complete if started.
This code may solve your problem.
This kind of problem accord bez of async nature of js.
function asyncLoop(i, rows, cb) {
if (i < rows.length) {
rules.checRules(rows[i], myhttp, function (rules_res) {
console.log(rules_res);
asyncLoop(i + 1, rows, cb);
//your code
});
} else {
cb();
}
}
function MasterGet() {
connection.query(
'SELECT * FROM MASTER',
function selectCb(error, rows, fields) {
if (error) {
console.log('Log 045 - GetData Error: ' + error.message);
return;
}
asyncLoop(0, rows, () => {
//after async loop complete...
});
});
countCheckRule++;
setTimeout(funcL, 4000);
}
Seems like a super basic task, but I just cannot get this to work (not very experienced with mongo or nodeJS).
I have an array of records. I need to check the DB to see if any records with a matching name already exist and if they do grab that record so I can update it.
Right now I am trying this
function hit_the_db(db, record_name, site_id) {
return new Promise((resolve, reject) => {
var record = db.collection('' + site_id + '_campaigns').find({name: record_name}).toArray(function(err, result) {
if (err) {
console.log('...error => ' + err.message);
reject(err);
} else {
console.log('...promise resolved...');
resolve(result);
}
});
console.log('...second layer of select successful, returning data for ' + record.length + ' records...');
return record;
});
}
This query works in another part of the app so I tried to just copy it over, but I am not getting any records returned even though I know there should be with the data I am sending over.
site_id is just a string that would look like ksdlfnsdlfu893hdsvSFJSDgfsdk. The record_name is also just a string that could really be anything but it is previously filtered so no spaces or special characters, most are something along these lines this-is-the-name.
With the names coming through there should be at least one found record for each, but I am getting nothing returned. I just cannot wrap my head around using mongo for these basic tasks, if anyone can help it would be greatly appreciated.
I am just using nodeJS and connecting to mongoDB, there is no express or mongoose or anything like that.
The problem here is that you are mixing callback and promises for async code handling. When you call:
var record = db.collection('' + site_id + '_campaigns').find({name: record_name}).toArray(function(err, result) {
You are passing in a callback function, which will receive the resulting array of mongo records in a parameter called result, but then assigning the immediate returned value to a variable called 'record', which is not going to contain anything.
Here is a cleaned up version of your function.
function hit_the_db(db, site_id, record_name, callback) {
// Find all records matching 'record_name'
db.collection(site_id + 'test_campaigns').find({ name: record_name }).toArray(function(err, results) {
// matching records are now stored in 'results'
if (err) {
console.log('err:', err);
}
return callback(err, results);
});
}
Here is optional code for testing the above function.
// This is called to generate test data
function insert_test_records_callback(db, site_id, record_name, insert_count, callback) {
const testRecords = [];
for (let i = 0; i < insert_count; ++i) {
testRecords.push({name: record_name, val: i});
}
db.collection(site_id + 'test_campaigns').insertMany(testRecords, function(err, result) {
return callback(err);
});
}
// This cleans up by deleting all test records.
function delete_test_records_callback(db, site_id, record_name, callback) {
db.collection(site_id + 'test_campaigns').deleteMany({name: record_name}, function(err, result) {
return callback(err);
});
}
// Test function to insert, query, clean up test records.
function test_callback(db) {
const site_id = 'ksdlfnsdlfu893hdsvSFJSDgfsdk';
const test_record_name = 'test_record_callback';
// First call the insert function
insert_test_records_callback(db, site_id, test_record_name, 3, function(err) {
// Once execution reaches here, insertion has completed.
if (err) {
console.log(err);
return;
}
// Do the query function
hit_the_db(db, site_id, test_record_name, function(err, records) {
// The query function has now completed
console.log('hit_the_db - err:', err);
console.log('hit_the_db - records:', records);
delete_test_records_callback(db, site_id, test_record_name, function(err, records) {
console.log('cleaned up test records.');
});
});
});
}
Output:
hit_the_db - err: null
hit_the_db - records: [ { _id: 5efe09084d078f4b7952dea8,
name: 'test_record_callback',
val: 0 },
{ _id: 5efe09084d078f4b7952dea9,
name: 'test_record_callback',
val: 1 },
{ _id: 5efe09084d078f4b7952deaa,
name: 'test_record_callback',
val: 2 } ]
cleaned up test records.
I am trying to loop through an array and find the amount of tickets assigned to each person.
Unfortunately, I noticed that my taskcount is getting the same values but in different order, because of its asynchronous nature.
Some queries might take long and so the ones that gets finished first gets inserted and hence my array has the same values but in different order. Now, I want to avoid that and make it so, that once a query gets completed, only then the next value from the array is being picked up and pushed to search from the db. How can i modify my existing code.
exports.find_task_count = function(callback) {
var names = ['Evan', 'Surajit', 'Isis', 'Millie', 'Sharon', 'Phoebe', 'Angel', 'Serah']
var taskcount = []
var resultsCount = 0;
for (var i = 0; i < names.length; i++) {
_tasks.find({'assignee': names[i]}, function (err, tickets) {
resultsCount++
if (err) {
console.log(err)
return callback(err)
} else {
taskcount.push(tickets.length)
if (resultsCount === names.length) {
return callback(taskcount);
taskcount=[]
}
}
})
}
}
You can use the async module designed to handle such scenarios.
I have updated the code as follows
var async = require('async');
exports.find_task_count = function (callback) {
var names = ['Evan', 'Surajit', 'Isis', 'Millie', 'Sharon', 'Phoebe', 'Angel', 'Serah'];
async.map(names, function (name, iterateeCallback) {
_tasks.find({ 'assignee': name }, function (err, tickets) {
if (err) {
return iterateeCallback(err);
}
return iterateeCallback(null, tickets.length);
});
}, function (error, results) {
if (error) {
return callback(error);
}
return callback(null, results);
});
}
As per the documentation of async
Note, that since this function applies the iteratee to each item in parallel, there is no guarantee that the iteratee functions will complete in order. However, the results array will be in the same order as the original coll.
if you still want to process the array in series use mapSeries instead of map in the above code
I have an array of Courses [course1, course2, ..., courseN]. Each course has a Hero UUID, which matches the UUID of an object in another collection.
// This is actually another db query but imagine it's an array
var courses = [{
"courseName": "Sample course name 1",
"hero": "a3f6f088-7b04-45e8-8d3b-d50c2d5b3a2d"
}, {
"courseName": "Sample course name 2",
"hero": "1b46227a-c496-43d2-be8e-1b0fa07cc94e"
}, {
"courseName": "Sample course name 3",
"hero": "c3bae6bf-2553-473a-9f30-f5c58c4fd608"
}];
I need to iterate over all courses, get the hero uuid and do a query to the Heroes collection then when the query is complete add the hero information to the course object.
The problem is that all queries are fired so rapidly that MongoDB returns them in arbitrary order. It receives all 3 hero uuids in order but it will sometimes return the third one before the first one, etc. Is there a way for one query to complete then do the other one, etc.?
What I am doing right now is:
var newCourses = courses;
var counter = 0;
courses.forEach(function (course) {
var courseHeroUuid = course.hero;
// This function does the query by uuid and returns the doc
getHeroByUuid(courseHeroUuid, function (err, result) {
if (err) {
next(err);
}
// Replace the hero UUID with the hero document itself
newCourses[counter].hero = result[0];
if (++counter == courses.length) {
next(null, newCourses);
}
}
});
This is a function inside an async.waterfall array, this is why I track the counter and call next() to go on. I know I can use async.each for the iteration, I tried it didn't help out.
This is the query I am doing.
function getHeroByUuid(heroUuid, callback) {
Hero.find({uuid: heroUuid}, function (err, result) {
if (err) {
callback(err);
}
callback(null, result);
})
}
This happens:
http://i.imgur.com/mEoQfgH.png
Sorry to answer my own question but I figured it out. What I needed was right under my nose.
I ended up using the async.whilst() function, documentation is right here and does exactly what I need - execute the next iteration of the loop after the result from the previous one is returned.
My code now looks like this:
var newCourses = courses;
var courseItemsLength = courses.length;
var counter = 0;
async.whilst(
function () {
return counter < courseItemsLength;
}, function (callback) {
var heroUuid = allCourses[counter].hero;
getHeroByUuid(heroUuid, function (err, result) {
if (err) {
next(err);
}
newCourses[counter].hero = result.name;
counter++;
if (err) {
callback(err);
}
callback();
});
}, function (err) {
if (err) {
next(err);
}
next(null, newCourses);
});
I need to query rows from a database, process some information per row, and then update each row with the result.
This is my example code where the intention is to loop over each row and update the label:
var mysql = require('mysql');
var db = mysql.createConnection(config.database);
db.connect(function() {
db.query('SELECT id FROM testTable', function (err, rows) {
if (err) {
console.log(err);
} else {
if (rows.length) {
for (var i = 0, len = rows.length; i < len; i++) {
var row = rows[i];
console.log(row);
var label = "Label_"+row.id;
db.query('UPDATE testTable SET label = ? WHERE id = ?', [label, row.id], function(err, result) {
if (err) {
console.log(err);
} else {
console.log("Set label on row %s", row.id);
}
})
}
}
}
})
});
The output of this is:
{ id: 1 }
{ id: 2 }
{ id: 3 }
{ id: 4 }
Set label on row 4
Set label on row 4
Set label on row 4
Set label on row 4
So, as you can see, I've updated row 4 four times instead of four rows once each. Whilst I new the queries would be non-blocking, I thought the values would change for each one.
I know I can change my code to use rows.forEach(function(){...}) and that then executes each UPDATE one after the other and that would be ok. But to help my understanding I would like to know how I can correctly execute the updates asynchronously.
Your row variable is a closure in the callback function. The callback function doesn't get called until you've looped through all your results list. The sql queries are correct, but printing out the value of row.id in each callback just gives you the last iteration of the for loop each time because that is the state of the closure for every callback.
You can avoid this by using the underscore module. It can also help in making you logic simpler.
npm install underscore
Then your code would look like this:
var mysql = require('mysql');
var _ = require('underscore');
var db = mysql.createConnection(config.database);
db.connect(function() {
db.query('SELECT id FROM testTable', function (err, rows) {
if (err) { console.log(err); return; }
_.each(rows, function(one) {
console.log(one);
var label = "Label_"+one.id;
var sql = 'UPDATE testTable SET label = ? WHERE id = ?';
db.query(sql, [label, one.id], function(err, result) {
if(err) { console.log(err); return; }
console.log("Set label on row %s", one.id);
});
});
});
});