I have a series of queries that need to run in a specific order. I've been trying this:
var queries = []
queries.push('update blah set foo="bar"')
queries.push('update baz set bar="foo"')
for(var i=0; i< queries.length; i++){
Promise.all([
migration.sequelize.query(queries[i]).then(function(result){
console.log(result)
})
])
}
done();
This does not work as anticipated. Any suggestions?
UPDATE: using recursion in the callback seems to work
var queries = []
queries.push('update blah set foo="bar"')
queries.push('update baz set bar="foo"')
var index = 0
var execute = function(queries){
if(typeof queries[index] == 'undefined'){
return done()
}
console.log(queries[index])
migration.sequelize.query(queries[index]).then(function(result){
console.log(result)
index += 1
return execute(queries)
})
}
execute(queries)
If you're on io.js and can write generators, or transpiling using Babel or TypeScript and can write async functions, this becomes very easy.
async function runSerialQueries(queries) {
var results = [];
for (var i=0; i<queries.length; i++) {
var query = queries[i];
var result = await migration.sequelize.query(query);
results.push(result);
}
return results;
}
runSerialQueries([
'update blah set foo="bar"',
'update baz set bar="foo"'
]).then(function(results) {
// ...
})
NOTE: The async keyword used above is a native feature of JavaScript EcmaScript 2016, not to be confused with the "async" npm module which is a completely different thing.
Anyhow, this solution ensures that one query doesn't start until the previous one ends, and will go in the order of the original array. It basically behaves just like it reads. If you can write generators but not async functions, something nearly identical can be done with generators, but requires a library like co() or Bluebird.coroutine().
Check out this excellent article for background on these techniques. They really are the future of JavaScript:
https://blog.risingstack.com/asynchronous-javascript/ <-- highly recommended!
I don't exactly know what the problem is with your code (if there any).
You should try with the each function of the async lib.
For example:
async.each(queries, function(query, done) {
// do the query here
done(); // this query is done, so runs the next one
});
Related
I have some dynamic data that needs to have work performed on it. The work must happen sequentially. Using the Q Library, I'd like to create an array of functions and execute the code sequentially using sequences. I can't seem to quite figure out the syntax to achieve this.
const fruits = ["apple", "cherry", "blueberry"]
function makeFruitPie (fruit) {
return Q.Promise((resolve, reject) => {
// Do some stuff here
resolve(fruit+" pie")
// Error handling here
reject(new Error(""))
})
}
const fruitFuncs = new Array(fruits.length)
for(var i = 0; i < fruits.length; i++) {
fruitFuncs[i] = makeFruitPie(fruits[i])
}
// Stole this example from the github docs but can't quite get it right.
i = 0
var result = Q(fruits[i++])
fruitFuncs.forEach((f) => {
result = result(fruits[i++]).then(f)
})
With these lines
for(var i = 0; i < fruits.length; i++) {
fruitFuncs[i] = makeFruitPie(fruits[i])
}
you already run the functions and, hence, their processing will begin.
Assuming you want the execution of the functions in sequence, the following would be more appropriate:
// construct the pipeline
const start = Q.defer();
let result = start.promise; // we need something to set the pipeline off
fruits.forEach( (fruit) => {
result = result.then( () => makeFruitPie( fruit ) );
});
// start the pipeline
start.resolve();
Sidenote: There is a native Promise implementation supported by almost all environments. Maybe consider switching from the library backed version.
You can use Promise.all
Promise.all(fruits.map(fruit=>makeFruitPie(fruit).then(res=> res) )).
then(final_res => console.log(final_res))
final_res will give you array of results
you could use for..of and do things sequentially. something like this
const Q = require("q");
const fruits = ["apple", "cherry", "blueberry"];
function makeFruitPie(fruit) {
return Q.Promise((resolve, reject) => {
// Do some stuff here
resolve(`${fruit} pie`);
// Error handling here
reject(new Error(""));
});
}
for (const fruit of fruits) {
const result = await makeFruitPie(fruit);
console.log(result);
}
By the way also worth considering native Promise insteead of using q
NodeJS 6.9.3
What I previously had went like this:
An outer function called "get_user()":
return database_queries.get_user(user_name)
.then(function(results_from_database) {
and that function then ran a database call, using Knex, and returned:
var dbquery = Multiline.stripIndent(function () {/*
SELECT
u.id as profile_id,
'user' as type_of_profile
FROM
user_profile u
WHERE name REGEXP "[[:<:]]||user_name||[[:>:]]"
*/});
dbquery = dbquery.replaceAll('||user_name||', user_name);
return DB.knex.raw(dbquery).then(function(result1) {
for(var index_of_results = 0; index_of_results < result1[0].length; index_of_results++) {
var document1 = result1[0][index_of_results];
array_of_maps_with_profile_type_and_profile_id[document1["type_of_profile"]].push(document1["profile_id"]);
}
When I did this, the database query ran, and got data, but this happened asynchronously, without the results ever being returned to the outer function. In other words, the outer function had completed long before the database queries had run.
So I tried to wrap the inner function in a Promise:
function get_user(user_name) {
return new Promise(function(resolve, reject) {
resolve ()
.then(function() {
var dbquery = Multiline.stripIndent(function () {/*
SELECT
u.id as profile_id,
'user' as type_of_profile
FROM
user_profile u
WHERE name REGEXP "[[:<:]]||user_name||[[:>:]]"
*/});
dbquery = dbquery.replaceAll('||user_name||', user_name);
return DB.knex.raw(dbquery).then(function(result1) {
for(var index_of_results = 0; index_of_results < result1[0].length; index_of_results++) {
var document1 = result1[0][index_of_results];
array_of_maps_with_profile_type_and_profile_id[document1["type_of_profile"]].push(document1["profile_id"]);
}
Now the database calls don't seem to ever be called. When they run, they appear in the logs, but now there are no database queries appearing in the logs. It would seem this inner function now returns a Promise, but the "resolve()" part of the Promise is never called.
What am I doing wrong here?
Here is a bit simpler way to write essentially the same query:
function get_user(user_name) {
const regex = `[[:<:]]${user_name}[[:>:]]`;
return DB.knex('user_profile')
.where(DB.knex.raw(`?? REGEXP ?`, ['name', regex]))
.then(res => {
// do what ever you like with returned rows here
});
}
You might want to simplify that a bit.
Take a look at these example projects.
https://github.com/joicenunes/helloapp
https://github.com/joicenunes/exercicio02
https://github.com/joicenunes/exercicio03
https://github.com/joicenunes/exercicio-04
(there are more, but you can find the rest)
Also avoid "replaceAll" and use binding variables.
Finally, since you are using node 6.x, you can use a few es6 goodies (arrow functions, multi-line strings, classes, etc), make the language work for you.
I am writing a simple server in NodeJS without using any frameworks except database connection. I have this code to populate database:
module.exports = function(callback) {
var model = require('./model');
var seedData = [
// Some seed objects
];
var count = 0;
for(var i = 0; i < seedData.length; i++) {
model.Complaint.createComplaint(seedData[i], function(err, id) {
count++;
if (count === seedData.length) {
callback();
}
});
}
};
Here I check in each callback if other callbacks have been executed. If count is the length of seedData array, I call the main callback. Is it a good way to manage loop which calls async methods?
As ShanShan already mentioned in the comment, Promises are really powerful when you need to work with a lot of asynchronous functions. If you're just looking to simplify your current script a bit, Async.js may be useful for you. It allows you to rewrite your loop as:
async.each(seedData, model.Complaint.createComplaint, callback);
And provides a lot of other methods which give you more control over the flow (such as running the functions either in series or parallel).
Or quite possibly I am doing it wrong, in fact, more than likely I am doing it wrong.
Have a table which contains a "tree" of skill, starting at the root level and may be as deep as ten levels (only two so far), but I want to return it as one big fat JSON structure, so I want to ask the database for each set of data, build my structure then ask for the next level.
Of course if I just send of my requests using mongoose, they will come back at any time, as they are all nice asyncronous calls. Normally a good things.
Looking at the documentation for Mongoose(using 4.1.1) it seems like it has a promise built in, but whenever I try to use it the api call throws a hissy fit and I get a 500 back.
Here is my simple function:
exports.getSkills = function(req,res) {
console.log("Will return tree of all skills");
for (var i = 0; i<10; i++){
var returnData = [];
console.log("Lets get level " + i );
var query = Skill.find({level: i });//The query function
var promise = query.exec; //The promise?
promise.then(function(doc) { //Totally blows up at this point
console.log("Something came back")
return "OK";
});
}
}
The Mongoose documentation on the subject can be found here
http://mongoosejs.com/docs/api.html#promise_Promise
var promise = query.exec;
// =>
var promise = query.exec()
exports.getSkills = function(req,res) {
console.log("Will return tree of all skills");
var p;
for (var i = 0; i < 10; i ++) {
if (i == 0 ) {
p = Skill.find({level:i}).exec();
} else {
p.then(function (){
return Skill.find({level:i}).exec()
})
}
p.then(function (data) {
//deal with your data
})
}
p.then(function () {
// deal with response
})
}
Using Node.js monk and MongoDB, I want to mimic a table join:
Do a find on collection A
For each result X, do a find in collection B, and update X
Return updated list of results
The asynchronous nature of database commands in monk is giving me trouble.
This is my initial code. It doesn't work because the second call to find returns a promise immediately,
and the results in xs are sent in the response before they can be updated.
var db = require('monk')('localhost/mydb');
db.get('collection').find({}, function(e,xs) {
xs.forEach(function(x){
coll_b.find({a_id:x._id}, function(e,bs) {
a['bs'] = bs;
});
});
res.json({'results':as});
});
I feel like I should use promise chaining here, but I cannot figure out how to do it.
Any help would be greatly appreciated.
I think I solved it in this way, inspired by this answer:
var db = require('monk')('localhost/mydb');
// Initial find
db.get('collection').find({}, function(e,xs) {
// Get inner finds as a list of functions which return promises
var tasks = xs.map(function(x){
return function() {
return coll_b.find({a_id:x._id}, function(e,bs) {
a['bs'] = bs;
});
}
});
// Chain tasks together
var p = tasks[0](); // start the first one
for(var i = 1; i < tasks.length; i++) p = p.then(tasks[i]);
// After all tasks are done, output results
p.then(function(_x){
res.json({'results':xs});
});
});
I still feel like this code could be minimised by using chain(), but at least this works as expected.
Note: I realise that performing a second find for each result is not necessarily efficient, but that's not my concern here.