I need to read values from an SQLite database in my plugin. For that I found the sqlite.jsm module. My problem is I want to make a row as a global variable, but the code used in SQLite (promises and tasks) is asynchronous. Is there a way I can collect information from my database into a global variable?
let iDs = [];
Task.spawn(function* () {
let db = yield Sqlite.openConnection({ path:
permissionFilePath});
try {
let row = yield db.execute(
"SELECT id FROM 'moz_hosts'");
for ( i=0; i < row.length; i++) {
console.log("row["+ i +"] :" +
row[i].getResultByIndex(0));
yield iDs.push(row[i].getResultByIndex(0));
}
}
finally {
yield db.close();
}
});
// Part of the code that doesn't work, because IDs are not yet assigned any values!
console.log("debug");
for (i=0; i<iDs.length; i++) {
yield console.log("iDs ["+i+"] = "+ iDs[i]);
}
First of all, for a Task you only have to yield things that return promises and therefore run asynchronously. There is no need to yield iDs.push(row[i].getResultByIndex(0));, because the push operation will synchronously return the new length of the array. This shouldn't be much of an issue for the code itself though.
Do you really need the ids to be global? Maybe you can refactor your code so that you don't need to save them globally.
If this is not an option, you will have to block all operations that are going to access the ids until the SQL call has completed. You can do this by relying on the fact that Task.spawn() itself will also return a promise. This also has the nice side effect that you don't need the extra global array:
let idsPromise = Task.spawn(function*() {
let ids = [];
let db = yield Sqlite.openConnection({ path: permissionFilePath});
try {
let row = yield db.execute("SELECT id FROM 'moz_hosts'");
for (let i = 0, len = row.length; i < len; i++) {
ids.push(row[i].getResultByIndex(0));
}
// Instead of the loop you can also use:
// ids = row.map(row => row.getResultByIndex(0));
} finally {
yield db.close();
}
return ids;
});
Then, in other parts of your code, when you want the ids you can use:
idsPromise.then(function(ids) {
// Do something with ids
});
Alternatively, you can also get them in a Task:
Task.spawn(function*() {
let ids = yield idsPromise;
});
You can do this multiple times. Once a promise is resolved the then() part will be executed as soon as possible.
Related
I have a collection, and now I need to iterate through the data in it, and then modify one of the fields, but this modification process is time consuming and requires asynchronous, I do not know how to write this code correctly.
Here are my fake code:
Stream<int> asynchronousNaturalsTo(int n) async* {
yield calculate(n).asStream();
}
Future<int> calculate(int i) async {
// async calculate result. maybe there is some long-running operate.
return Future.value(i + 10);
}
main() async {
// 1. I have a List that contains data needs be calculated by async.
for (int i = 0; i < 3; i++) {
// 2. calculate
var list = await asynchronousNaturalsTo(i);
// 3. finally, use the final result.
print(list);
}
}
How do I write the code?
thx...
I have a function that creates a dictionary based on a series of values from a sheet. I then try to pull one of the values from that sheet using a key. It works fine to log to the console. However, in an if statement, it says syntax error and nothing else. I cannot figure it out. Here is the function and the code that crashes. This problem only occurs in the for loop, and does not occur outside of it.
//creates dictionary
function columnLocationWithNotation(notation) {
var spreadsheet = SpreadsheetApp.openByUrl();
var sheet = spreadsheet.getActiveSheet();
var data = sheet.getDataRange();
var cells = data.getValues();
var dictionary = {};
switch (notation) {
case "zeroIndex":
for (var i = 0; i < sheet.getLastRow(); i++) {
dictionary[cells[i][0]] = cells[i][1]
}
return dictionary
break;
case "regularIndex":
for (var i = 0; i < sheet.getLastRow(); i++) {
dictionary[cells[i][0]] = cells[i][2]
}
return dictionary
break;
case "string":
for (var i = 0; i < sheet.getLastRow(); i++) {
dictionary[cells[i][0]] = cells[i][3]
}
return dictionary
break;
}
}
var master0indexDictionary = columnLocationWithNotation("zeroIndex")
for (var i = 1; i =< (sheet.getLastRow() - 1); i++) {
var phone = master0indexDictionary["Tutor Name"]
if (cells[i][phone] === phoneNumber) { //LINE WITH SYNTAX ERROR
//do something
}
It's not the highlighted line that is causing the problem, even though there are many other issues with your script. There's no '=<' operator in JavaScript. Use '<=' instead:
for (var i = 1; i <= (sheet.getLastRow() - 1); i++) {
Also, as Tanaike pointed out, your 'cells' variable is only defined in the context of your 'columnLocationWithNotation(notation)' function and will not be accessible from the global context. Globally-defined variables are visible from the functions you declare inside the global object, but not vice versa. Same applies to the 'sheet' variable. The 'phoneNumber' variable doesn't seem to be defined, at least not in the snippet of code you provided.
Note that putting 'break' after 'return' statements is redundant.
return dictionary;
break;
You can just return out of 'switch' statement without using breaks, or leave the breaks and put a single 'return' statement after 'switch'.
Finally, always put a semicolon at the end of the line. Doing so will help you avoid many potential pitfalls and issues with JS parser. I noticed several instances where you omitted the semicolon:
return dictionary
break;
var phone = master0indexDictionary["Tutor Name"]
For example, the following code will break if you don't have a habit of putting the semicolon in its rightful place
var a = {name: 'John'} //no semicolon
[a].forEach(function(element) {
Logger.log(element); //logs undefined
})
The JS parser treats this code as one line, so 'a' will still be 'undefined' by the time you call the 'forEach()' loop on the array.
I have a loop that has an asynchronous call inside it, with a callback. To be able to move on, I need the callback to fire for the entire loop all the way through, to then display the results of the loop.
Every way I've tried to control this doesn't work (have tried Step, Tame.js, async.js, and others) - any suggestions on how to move forward?
array = ['test', 'of', 'file'];
array2 = ['another', 'array'];
for(i in array) {
item = array[i];
document_ids = new Array();
for (i2 in array2) {
item2 = array2[i2];
// look it up
mongodb.find({item_name: item2}).toArray(function(err, documents {
// because of async,
// the code moves on and calls this back later
console.log('got id');
document_ids.push(document_id);
}))
}
// use document_ids
console.log(document_ids); // shows []
console.log('done');
}
// shows:
// []
// done
// got id
// got id
You're logging document_ids before your callbacks fire. You have to keep track of how many callbacks you've run to know when you're done.
An easy method is to use a counter, and to check the count on each callback.
Taking your example
var array = ['test', 'of', 'file'];
var array2 = ['another', 'array'];
var document_ids = [];
var waiting = 0;
for(i in array) {
item = array[i];
for (i2 in array2) {
item2 = array2[i2];
waiting ++;
mongodb.find({item_name: item2}).toArray(
function(err, document_id) {
waiting --;
document_ids.push(document_id);
complete();
})
);
}
}
function complete() {
if (!waiting) {
console.log(document_ids);
console.log('done');
}
}
I would like to descend a directory, and examine the name of every file I see there against a regular expression. Basically, a version of the common unix find command, only written in Node.js. I don't care about the order of the files, but I do want to make sure I get all of them.
I have the following code, which is close (I think) to what I want. It takes a startdir, a regexp, and a callback; for each file it analyzes it increments the value of sentinel by one, and when it is done with the analysis it decrements the sentinel. My concern is that if there's one file, and a deeply nested collection of directories, it will analyze that file and trigger the callback long before it finds a second file, and possible the callback will be called twice.
Obviously, I could prevent the callback from being called twice by having a fired variable to restrain it from firing a second time. But that still would give me erroneous data. What am I doing wrong here, and is there a more node-appropriate way to do it?
fs = require('fs')
path = require('path')
function get_all_files(startdir, regexp, callback) {
var sentinel = 0;
var results = [];
function check_sentinel() {
sentinel--;
if (sentinel === 0) {
callback(results);
}
}
function check_file(dir, filename) {
var fname;
sentinel++;
if (regexp.test(filename)) {
results.push(path.join(dir, filename));
}
check_sentinel();
}
function check_directory(dir) {
fs.readdir(path.join(this.rootpath, dirpath), function(err, files) {
var fname, i, len, npath;
if (err) {
throw err
}
for (i = 0, len = files.length; i < len; i++) {
fname = files[i];
npath = path.join(dir, fname);
fs.stat(npath, function(err, stats) {
if (stats.isFile()) {
check_file(dir, fname);
} else {
if (stats.isDirectory()) {
check_directory(npath);
}
}
});
}
});
}
check_directory(startdir);
}
A couple of thoughts...
I have never used it, but the simplest way to do what you are asking might be to use the async.js walkfiles function. See the tests for an example here.
Otherwise, I would consider building an array of function calls and returning the array from your recursive directory walking function (rather than using a sentinel, etc). In other words, check_directory returns an array of function calls matching the files you are looking for. If there is no file, the array is empty.
Finally, combine the array at the top of the recursion and use the async library (not the same as async.js) to execute the array of functions all at once using the parallel function (see this thread for an example using "series").
I have
public jsonresult update(studentinfo s)
{
for(i=0;i>0;i++)
{
var x = // i am getting some x so i am checking again
if( x != null)
{
var updateuser = student.update(s.student,"","");
**return json(updateuser.ToString());** // if i keep it here i am getting exceptoin saying not all code paths return value bec this return i can not keep it out for loop bec each and evary updateuser i need to return json..
}
}
}
How to overcome this type of things?
What language are you using to write your code? What you've posted doesn't look like any of the valid languages I know for .NET. Here's how the controller action might look in C# (assuming this is the language you are using):
public ActionResult Update(StudentInfo s)
{
// create some collection that will contain all updated users
var updatedUsers = new List<StudentInfo>();
// Revise the loop as it is absolutely not clear from your code
// what you are trying to do. The way you wrote the loop it will
// never execute - for(int i=0; i>0; i++)
for (int i = 0; i < 5; i++)
{
var updatedUser = student.Update(s.student, "", "");
updatedUsers.Add(updatedUser);
}
// return the list of updated users outside the loop so that the compiler
// doesn't complain about paths of the method not returning a value
return Json(updatedUsers);
}
If I understand correctly, you want to return a collection of users. The 'return' keyword does not work like that. You need to return the entire collection at once.