So considering I have a seeder like so:
var seedDatabase = function () {
var promises = [];
promises.push(createProductTypes());
promises.push(createCompensationTypes());
promises.push(createDataSources());
promises.push(createPeriodsAndStages());
promises.push(createExportDestinations());
return Promise.all(promises);
};
function createCompensationTypes() {
return models.sequelize
.transaction(
{
isolationLevel: models.sequelize.Transaction.ISOLATION_LEVELS.READ_COMMITTED
},
function (q) {
return models.CompensationType
.findAll()
.then(function (result) {
if (!result || result.length == 0) {
return models.CompensationType
.bulkCreate(compensationTypes, { transaction: q })
.then(function () {
console.log("CompensationTypes created");
})
}
else {
console.log("CompensationTypes seeder skipped, already objects in the database...");
return;
}
});
})
.then(function (result) {
console.log("CompensationTypes seeder finished...");
return;
})
.catch(function (error) {
console.log("CompensationTypes seeder exited with error: " + error.message);
return;
});
};
For some reason this hangs on execution. Yet, when I comment any one of the promises added to the array everything goes smoothly as it should.
Also when I do the following in my seedDatabase function, it works too:
var seedDatabase = function () {
var promises = [];
promises.push(createProductTypes());
promises.push(createDataSources());
promises.push(createPeriodsAndStages());
promises.push(createExportDestinations());
return Promise.all(promises).then(function() {
return createCompensationTypes()
});
};
which is bizarre since obviously my code is working but still hangs on an extra promise. Hence my question, is there a maximum of concurrent promises or transactions when using sequelizeJS? Or am I overlooking something else here?
Related
Sorry if my title is not very explicit I dont know how to explain this properly.
I am trying to use the distinct function for my app that uses loopback 3 and mongodb. It seems to work right but my endpoint wont hit the return inside my function.
This is my code
const distinctUsers = await sellerCollection.distinct('userId',{
hostId : host.id,
eventId:{
"$ne" : eventId
}
}, async function (err, userIds) {;
if(!userIds || userIds.length ==0)
return [];
const filter = {
where:{
id: {
inq: userIds
}
}
};
console.log("should be last")
return await BPUser.find(filter);
});
console.log(distinctUsers);
console.log("wtf??");
//return [];
If I uncomment the return [] it will send the return and later it will show the should be last, so even when I dont have the return it seems to finish. It is now waiting for the response. I dont like the way my code looks so any pointer of how to make this look better I will take it.
It looks like the sellerCollection.distinct takes a callback as one of it's parameters, therefore, you cannot use async/await with a callback-style function, since it's not a promise.
I would suggest turning this call into a promise if you'd like to use async/await:
function findDistinct(hostId, eventId) {
return new Promise((resolve, reject) => {
sellerCollection.distinct(
'userId',
{ hostId, eventId: { "$ne": eventId } },
function (error, userIds) {
if (error) {
reject(error);
return;
}
if (!userIds || userIds.length === 0) {
resolve([]);
return;
}
resolve(userIds);
}
)
})
}
Then, you can use this new function with async/await like such:
async function getDistinctUsers() {
try {
const hostId = ...
const eventId = ...
const distinctUsers = await findDistinct(hostId, eventId)
if (distinctUsers.length === 0) {
return
}
const filter = {
where: {
id: { inq: userIds }
}
}
const bpUsers = await BPUser.find(filter) // assuming it's a promise
console.log(bpUsers)
} catch (error) {
// handle error
}
}
Response.json should execute after foreach loop completes its execution
var todoarr = (req.body.data) ? req.body.data : undefined
todoarr.forEach(function(element) {
if(element.done == true) {
TodoService.removeTodo(element, function(success) {
});
}
});
res.json("success");
You can try to use async.js http://caolan.github.io/async/ .
each method http://caolan.github.io/async/docs.html#each
Or you can try use Promise.all.
For example:
let promiseArr = [];
todoarr.forEach(function(element) {
if(element.done == true) {
promiseArr.push(somePromiseMethod(element));
}
});
//now execute promise all
Promise.all(promiseArr)
.then((result) => res.send("success"))
.catch((err) => res.send(err));
More info here. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
Some promise example:
function somePromiseMethod(element) {
return new Promise((resolve,reject) => {
TodoService.removeTodo(element, function(success) {
resolve();
});
});
}
Hope this helps.
You can't send multiple responses on single request, the only thing you can do it's a single response with the array of results:
es with async:
const async = require('async')
// FIX ME: this isn't correctly handled!
const todoarr = (req.body.data) ? req.body.data : undefined
let results = []
async.each(todoarr, function(element, callback) {
console.log('Processing todo ' + element)
if(element.done == true) {
TodoService.removeTodo(element, function(err, success) {
if(err){
callback(err)
} else {
results.push(success)
callback(null, success)
}
})
}
}, function(err) {
if(err) {
console.log('A element failed to process', err)
res.status(500).json(err)
} else {
console.log('All elements have been processed successfully')
// array with the results of each removeTodo job
res.status(200).json(results)
}
})
You can send the response inside the callback function of forEach.
Modify your function so that it will call res.json() on the last iteration only.
Example:
var todoarr = (req.body.data) ? req.body.data : undefined
todoarr.forEach(function(element,index) {
if(element.done == true) {
TodoService.removeTodo(element, function(success) {
});
}
if(index==todoarr.length-1){
res.json("success");
}
});
However, it may not be according to coding standards but it can definitely solve the problem.
var data = [10,21,33,40,50,69];
var i = 0;
var dataSeq = [];
while(i<data.length){
if(data[i]%2 == 0){
store.findOne({'visibility': true},function(err, data){
dataSeq.push(i)
i++;
});
}
else{
dataSeq.push(i)
i++;
}
}
if(i==data.length-1){
console.log(dataSeq) // Should Print [1,2,3,4,5]
return res.status(200).send({ message: 'Task Completed'})
}
I want to collect data as per loop excecutes.
I am aware about how to handle async calls in nodejs. But I want the callbacks in sequence.
e.g. Though there is a async call in if condition i want to hault the loop, so that I can push value of i in dataSeq and it will result in [1,2,3,4,5] array. I want that sequence because my post operations are dependent on that sequence.
I think asyncjs#eachSeries has what you need.
Your code would become something like this:
async.each(data, (item, callback) => {
if(item%2 == 0){
store.findOne({'visibility': true},function(err, data){
dataSeq.push(i)
i++;
});
}
else{
dataSeq.push(i)
i++;
}
}, (err) => {
// if any of the callbacks produced an error, err would equal that error
});
You can use something like async#eachOf
var async = require('async');
var data = [10,21,33,40,50,69];
var dataSeq = [];
async.eachOf(data, function(value, key, cb) {
if (value % 2 == 0) {
store.findOne({ 'visibility': true })
.then(function(doc) {
dataSeq.push(key);
})
.catch(function(err) {
return cb(err);
});
} else {
cb();
}
}, function(err) {
if (err) {
console.error(err)
return res.status(500).send(); # handle the error as you want
}
return res.status(200).send({ message: 'Task Completed'})
})
socket.on('new video', function(data) {
console.log("New video")
var addToCueP = Promise.promisify(addToCue)
var getCueFromDbP = Promise.promisify(getCueFromDb)
addToCueP(data.id, socket.nickname)
.then(function() {
return getCueFromDbP();
})
.then(function() {
console.log("Emit change video")
io.sockets.emit('change video', {id: data.id, title: data.title, nick: socket.nickname});
})
});
I am using Bluebird for promises but I'm having issues, the addToCue function gets called but the other functions after for example the getCuefromDb and the Socket emitter isnt getting triggered, does anyone have any idea where I'm going wrong?
addToCue()
var addToCue = function(id, user) {
console.log("Add to cue")
var video = new Video();
video.id = id;
video.user = user;
video.save(function(err, data) {
if (err) {
console.log(err)
} else {
return true
}
});
}
getCueFromDb()
var getCueFromDb = function(callback) {
Video.find({}).exec(function(err, videos) {
if (err) {
return err;
}
if (videos.length) {
cue = []; // empty array
videos.forEach(function(video) {
cue.push(video.id) // push all the videos from db into cue array
});
io.sockets.emit('send cue', {cue: cue});
console.log("getCueFromDb", cue)
if (callback) {
callback();
return
}
else {
return
}
}
else {
cue = [];
io.sockets.emit('send cue', {cue: cue});
console.log("getCueFromDb (no videos)", cue)
if (callback)
callback()
else
return
}
})
}
addToCue() does not accept the right types of arguments in order to call .promisify() on it- therefore promisify does not work properly with it.
The last argument to addToCue() MUST be a callback that is called when the async operation is done and the first argument to that callback must be an error value and the second argument can be a result.
Now, since you control the code to addToCue(), you should just change its code to return a promise that is resolved/rejected based on the underlying async operation and not use .promisify() at all.
var addToCue = function(id, user) {
return new Promise(function(resolve, reject) {
console.log("Add to cue")
var video = new Video();
video.id = id;
video.user = user;
video.save(function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
Alternatively, you could use .promisify() on video.save() or .promisifyAll() on the Video object.
Or, if your Video interface already supports promises, you can just use it's native support and return that promise.
In an attempt to grasp Q.js, I'd like to convert the following code using async.series in Q.js. Basically I create a folder if it doesn't exist (using mkdirp), move a file into a backup folder and save a file into a main folder.
var async = require('async');
var fs = require('fs');
var path = require('path');
var sessiondId = new Date().getTime() % 2 == 0 ? new Date().getTime().toString() : '_1234';
var backupFolder = path.join(__dirname,sessiondId);
var backupFullPath = path.join(backupFolder,'a.txt');
var fullPath = path.join(__dirname,'main','a.txt');
var mkdirp = require('mkdirp');
async.series({
createOrSkip: function(callback) {
mkdirp(backupFolder, function (err, dir) {
if(err) {
callback(err, null);
} else {
callback(null, {created: !!dir, folderAt: backupFolder});
}
});
},
move: function(callback) {
fs.rename(fullPath, backupFullPath, function(err) {
if(err) {
callback(err, null);
} else {
callback(null, {backupAt: backupFullPath});
}
});
},
write: function(callback) {
fs.writeFile(fullPath, 'abc', function(err) {
if (err) {
callback(err, null);
} else {
callback(null, {saveAt: fullPath});
}
});
}
}, function(err, result) {
console.log(result);
});
Actually I don't know where to start. Thanks for your help.
R.
The key is to convert the node.js functions to return promises using Q.denodeify before you start, this means the header of your file should look like:
var Q = require('q')
var fs = require('fs');
var path = require('path');
var sessiondId = new Date().getTime() % 2 == 0 ? new Date().getTime().toString() : '_1234';
var backupFolder = path.join(__dirname,sessiondId);
var backupFullPath = path.join(backupFolder,'a.txt');
var fullPath = path.join(__dirname,'main','a.txt');
var mkdirp = Q.denodeify(require('mkdirp'));
var rename = Q.denodeify(fs.rename);
var writeFile = Q.denodeify(fs.writeFile);
That change wouldn't be needed if node.js natively supported promises.
Option 1
// createOrSkip
mkdirp(backupFolder)
.then(function (dir) {
// move
return rename(fullPath, backupFullPath);
})
.then(function () {
// write
return writeFile(fullPath, 'abc');
})
.done(function () {
console.log('operation complete')
});
I don't think it gets much simpler than that. Like #Bergi said though, it's more similar to "waterfall". If you want the exact behavior of series (but with promises) you'll have to use something like Option 2 or Option 3.
Option 2
You could write out the code manually to save the results. I usually find that, although this requires a little extra writing, it's by far the easiest to read:
var result = {}
mkdirp(backupFolder)
.then(function (dir) {
result.createOrSkip = {created: !!dir, folderAt: backupFolder};
return rename(fullPath, backupFullPath);
})
.then(function () {
result.move = {backupAt: backupFullPath};
return writeFile(fullPath, 'abc');
})
.then(function () {
result.write = {saveAt: fullPath};
return result;
})
.done(function (result) {
console.log(result);
});
Option 3
If you find yourself using this sort of code all the time, you could write a very simple series helper (I've never found the need to do this personally):
function promiseSeries(series) {
var ready = Q(null);
var result = {};
Object.keys(series)
.forEach(function (key) {
ready = ready.then(function () {
return series[key]();
}).then(function (res) {
result[key] = res;
});
});
return ready.then(function () {
return result;
});
}
promiseSeries({
createOrSkip: function () {
return mkdirp(backupFolder).then(function (dir) {
return {created: !!dir, folderAt: backupFolder};
});
},
move: function () {
return rename(fullPath, backupFullPath)
.thenResolve({backupAt: backupFullPath});
},
write: function () {
return writeFile(fullPath, 'abc')
.thenResolve({saveAt: fullPath});
}
}).done(function (result) {
console.log(result);
});
I'd say once you've written the helper, the code is a lot clearer for promises than with all the error handling cruft required to work with callbacks. I'd say it's clearer still when you either write it by hand or don't keep track of all those intermediate results.
Summing Up
You may or may not think these examples are clearer than the async.series version. Consider how well you might know that function though. It's actually doing something pretty complex in a very opaque manner. I initially assumed that only the last result would be returned (ala waterfall) and had to look it up in the documentation of Async. I almost never have to look something up int the documentation of a Promise library.
Make each of your functions return a promise. Construct them with a Deferred:
function createOrSkip(folder) {
var deferred = Q.defer();
mkdirp(folder, function (err, dir) {
if(err) {
deferred.reject(err);
} else {
deferred.resolve({created: !!dir, folderAt: backupFolder});
}
});
return deferred.promise;
}
However, there are helper functions for node-style callbacks so that you don't need to check for the err yourself everytime. With Q.nfcall it becomes
function createOrSkip(folder) {
return Q.nfcall(mkdirp, folder).then(function transform(dir) {
return {created: !!dir, folderAt: backupFolder};
});
}
The transform function will map the result (dir) to the object you expect.
If you have done this for all your functions, you can chain them with then:
createOrSkip(backupfolder).then(function(createResult) {
return move(fullPath, backupFullPath);
}).then(function(moveResult) {
return write(fullPath, 'abc');
}).then(function(writeResult) {
console.log("I'm done");
}, function(err) {
console.error("Something has failed:", err);
});
Notice that this works like async's waterfall, not series, i.e. the intermediate results will be lost. To achieve that, you would need to nest them:
createOrSkip(backupfolder).then(function(createResult) {
return move(fullPath, backupFullPath).then(function(moveResult) {
return write(fullPath, 'abc');.then(function(writeResult) {
return {
createOrSkip: createResult,
move: moveResult,
write: writeResult
};
});
});
}).then(function(res){
console.log(res);
}, function(err) {
console.error("Something has failed:", err);
});