Bluebird promises - each function - node.js

Thank in advance for the help.
While using Bluebird promises, I have a series of promises running. During the last promise, I want to run one function multiple times for each object in an array.
Below there is the pseudocode:
var userArray = [
{
name: "John",
email: "John#email.com"
},
{
name: "Jane",
email: "jane#email.com"
}];
var functionOne = function() {
//returns Promsie object
};
var functionTwo = function() {
//returns promise object
};
var createUser = function(user) {
return User.findOrCreate({email: user.email},{
name: user.name,
email: user.email
});
};
functionOne()
.then(functionTwo)
.each(createUser(userArray))
.then(function onComplete() {
console.log("Complete");
})
.catch(function onError() {
console.log("Um...it's not working");
});
I know I'm not using the each function correctly. What's the correct way to implement this using Bluebird?

As I understand you want to take some asynchronous actions for elements from array. Then please check the following example:
var Promise = require('bluebird');
function createUsersFromArray(userArray){
return Promise.each(userArray, function(signleUser){
return createUserFunction(signleUser);
});
}
or
return Promise.each(userArray, createUserFunction);
functionOne()
.then(functionTwo)
.then(function(){
return createUsersFromArray(userArray);
})
//or just .then(createUsersFromArray) if functionTwo return this array
.then(function(createdUsers){
//here you may retrieve users and make some magic with them
console.log(createdUsers);
})
.then(function onComplete() {
console.log("Complete");
})
.catch(function onError() {
console.log("Um...it's not working");
});
I also recommend using "all" instead of "each"
Check the examples below:
return Promise.all(userArray.map(function(singleUser){
return doSomethingWithUser(singleUser);
}));
or
return Promise.all(userArray.map(doSomethingWithUser));
'all' will notify you if all the actions are taken correctly.
How to use promises (best practice):
http://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
https://blog.domenic.me/youre-missing-the-point-of-promises/

The most straightforward implementation:
functionOne()
.then(functionTwo)
.then(function(){
return bluebird.each(userArray, createUser);
})
.then(function onComplete() {
console.log("Complete");
})
.catch(function onError() {
console.log("Um...it's not working");
});
You should use .map instead of .each if you want to access the results of all of those creates.

Thanks #Roman #Yuri for the help! My now working code is below:
var userArray = [
{
name: "John",
email: "John#email.com"
},
{
name: "Jane",
email: "jane#email.com"
}];
var functionOne = function() {
//returns Promise object
};
var functionTwo = function() {
//returns Promise object
};
var createUser = function(singleUser) {
//returns Promise object containing creating User
};
functionOne()
.then(functionTwo)
.then(function() {
return Promise.map(userArray, createUser);
})
.then(function onComplete(response) {
console.log("Complete:" + JSON.stringify(response));
})
.catch(function onError() {
console.log("Um...it's not working");
});

Related

GraphQL Sent Proper Data but Variable is Undefined

Hopefully I'm not overlooking something stupid here, but here's the problem.
I'm fetching this Smartsheet data using promise:
var getWorkspaces = async function(args) {
var smartsheet = client.createClient({ accessToken: "key" });
await smartsheet.workspaces.listWorkspaces()
.then(async function(workspaceList) {
var data = await workspaceList.data;
console.log("returning");
console.log(data);
return data;
})
.catch(function(error) {
console.log(error);
});
}
and this is my resolvers:
var resolvers = {
Query: {
workspaces: async () => {
var workspaceData = await getWorkspaces();
console.log("sending");
console.log(workspaceData);
return workspaceData;
}
}
};
Why is this the console output:
returning
[
{
id: 000,
name: 'nname',
accessLevel: 'ADMIN',
permalink: 'link'
},
{
id: 000,
name: 'name',
accessLevel: 'ADMIN',
permalink: 'link'
},
{
id: 000,
name: 'name',
accessLevel: 'ADMIN',
permalink: 'link'
}
]
sending
undefined
And more specifically, why is workspaceList undefined? I've been at this for hours and cannot find a solution.
The issue here is that your return workspaceData is within a callback function of .then(), so you're returning in the callback, not in the getWorkspaces() function. When you say .next(), the request happens asynchronously. When you use await, the current thread waits for the response and executes it without needing a callback–– a synchronous call. await implicitly calls .next(), and adding try/catch is equivalent to .catch().
var getWorkspaces = async function(args) {
try {
var smartsheet = client.createClient({ accessToken: "key" });
var workspaceList = await smartsheet.workspaces.listWorkspaces();
var data = workspaceList.data;
console.log("returning");
return data;
} catch (err) {
console.log(err);
}
}

How to refactor promises all chain using async await Node js and sequelize ORM

I want to refactor code for chain of promises by async, await. I have sequelize ORM for DB management and the code is written in AWS Lambda function having multiple middleware. In such cases I have to traverse code for multiple entries using sequelize transactions. It is easy to manage using promise.all() but need to change it to async await syntax for cleaner code.
Here are my demo code.
/* get all invoice where user_id === current logged in user, and where status != "paid" */
db.Invoice.findAll({
where: {
user_id: currentLoggedInUser,
status: {
$ne: "paid"
}
}
}).then(invoices => {
if (!invoices || invoices === null) {
return false;
}
function addScheduledTransactionAttempts(invoice, tryPayOnDate, t) {
return new Promise((resolve, reject) => {
/* check If any ScheduledTransactionAttempts exists for this invoice.id */
db.ScheduledTransactionAttempts.find({
where: {
invoice_id: invoice.id
}
})
.then(function(attempts) {
if (attempts) {
attempts
.destroy({}, {
transaction: t
})
.then(deletedAttempts => {
console.log("Attempts Record Deleted: ", deletedAttempts);
})
.catch(error => {
reject(error);
t.rollback();
});
}
return db.ScheduledTransactionAttempts.create({
invoice_id: invoice.id,
payment_source_id: PaymentMethodId,
try_pay_on_date: tryPayOnDate,
stripe_customer_id: currentLogInStripeCustomerId
}, {
transaction: t
})
.then(function(attempt) {
resolve(attempt.id);
})
.catch(error => {
reject(error);
t.rollback();
});
})
.catch(error => {
reject(error);
t.rollback();
});
});
}
//Run transaction to addScheduledTransactionAttempts
return db.sequelize.transaction().then(function(t) {
let promiseArr = [];
var i = 0;
invoices.forEach(function(invoice) {
var schedulePaymentDate = moment(paymentDate);
if (invoice) {
let tryPayOnDate = schedulePaymentDate
.add(i, "month")
.format("YYYY-MM-DD");
promiseArr.push(
addScheduledTransactionAttempts(invoice, tryPayOnDate, t) //calling above function
);
i++;
}
});
//now execute promise all
Promise.all(promiseArr)
.then(function(result) {
t.commit();
return true;
})
.catch(function(err) {
t.rollback();
return false;
});
});
});
In the above code I want to change
Promise.all(promiseArr)
which is calling
addScheduledTransactionAttempts
function to do DB queries to simple async function await process to make it easy simpler understandable without having multiple .then or .then inside then promises.
Any help regarding would be appreciated,
Thanks.
It's quite simple. await is valid when invoking methods that return a Promise. All of your SDK methods already return a promise, so refactoring should be quite straight forward. Here's something to get you off ground:
const processInvoices = async currentLoggedInUser {
const invoices = await db.Invoice.findAll({
where: {
user_id: currentLoggedInUser,
status: {
$ne: 'paid',
},
},
});
if (yourOwnLogicForInvoicesObject) {
for (const invoice of invoices) {
const potentiallyFoundInvoice = await db.ScheduledTransactionAttempts.find({
where: {
invoice_id: invoice.id,
},
});
if (potentiallyFoundInvoice) {
await addScheduledTransactionAttempts(potentiallyFoundInvoice)
}
}
}
}
const addScheduledTransactionAttempts = async invoice => {
console.log('Do something with your invoice', invoice)
}
Long story short: refactor the code inside your functions into smaller functions and make these new functions async, just like I did with addScheduledTransactionAttempts and processInvoices
More on async/await

Stubbing a mongoose exec method using Bluebird and sinon in node.js

I have a service layer that calls a database layer where a query is run. I am trying to stub the data layer returning a value but I keep getting this error
TypeError: User.find(...).exec is not a function.
I am using the bluebird module and am also using sinon for stubbing
//Service Layer
var dbLayer = require('../dbLayer');
exports.getUsers = function (req, res) {
dbLayer.GetUsersThatNeedToBeApproved(req.body.domain)
.then(function (data) {
res.json(data);
})
.catch(function(error){
routesLogger.logError(req.session.user.role, "getUsers", error);
res.status(400).send({error: StatusCodes.DBERROR});
});
};
//DB Layer
module.exports.GetUsersThatNeedToBeApproved = function (domain) {
return new Promise.resolve(User.find({$and: [{'domain.upgradeRequest': true}, {'domain.access': constants.ACCESSPRIVATE}, {'domain.name': domain}]}).exec())
.then(function (data) {
return data;
}).catch(function (error) {
routesLogger.logError("userDAL", "GetUsersThatNeedToBeApproved", error);
return error;
});
This is my test case
describe("Suite to get users in portal that need to be approved",function(){
var request = null;
var response = httpMocks.createResponse();
it("Should get a list of all users that need to be approved for a specific domain",function(done){
request = {
session: {
user: {
domain:{
name:'domain'
},
email: 'testUser#test.com',
role: 'Admin'
}
}
};
var expectedFindResponse = [
{
email: "test#xxx.com",
role: "T",
domain:{
upgradeRequest:true,
access:'private',
name:'domain'
}
},
{
"email": "test1#xxx.com",
"role": "T",
domain:{
upgradeRequest:true,
access:'private',
name:'domain'
}
}
];
var find = sinon.stub(mongoose.Model, "find").returns(Promise.resolve(expectedFindResponse));
admin.getUsers(request, response);
var responseData = response._getData();
});
});

Trouble to synchronise promises in Node.js using Q

I am currently doing an API in Node.JS with the framework Sails.js. I am using promises for the first time and I have some troubles to sync my promises like I want.
My main function is the following :
createCard: function(req, res) {
checkIfUserHasStripeAccount(req.user)
.then(addCreditCardToStripeAccount())
.then(function cardCreated() {
res.send(200, {
msg: 'Card created'
});
})
.catch(function handleError(err) {
res.send(err.httpCode, err.msg);
})
},
Obviously I can't add a credit card to a stripe account if the user doesn't have one.
The function checkIfUserHasStripeAccount() checks if the account exists and if not, create it.
Here is the code for this part :
function checkIfUserHasStripeAccount(user) {
var deferred = q.defer();
if (!user.idStripe) {
createStripeAccountToUser(user)
.then(function(savedUser) {
deferred.resolve(savedUser);
})
.catch(function(err) {
deferred.reject(err);
})
} else {
deferred.resolve(user);
}
return deferred.promise;
}
function createStripeAccountToUser(user) {
var deferred = q.defer();
var jsonUserToCreate = {
description: user.firstname + ' ' + user.surname,
email: user.email
};
stripe.customers.create(jsonUserToCreate, function(err, customer) {
if (err) {
deferred.reject({
httpCode: 500,
msg: 'some error'
});
} else {
user.idStripe = customer.id;
user.save(function(err, savedUser) {
if (err) {
deferred.reject({
httpCode: 500,
msg: 'some error'
});
}
deferred.resolve(savedUser);
});
}
});
return deferred.promise;
}
The problem is that the .then(addCreditCardToStripeAccount()) is executed before checkIfUserHasStripeAccount() is finished.
I can't figure out why. I thought the .then(addCreditCardToStripeAccount()) would only be executed if it received a reject or resolve.
You are correct in your line of thought.
The problem is that you are invoking your function instead of referencing it:
.then(addCreditCardToStripeAccount())
should be:
.then(addCreditCardToStripeAccount)
I expect this to work:
createCard: function (req, res) {
checkIfUserHasStripeAccount(req.user)
.then(addCreditCardToStripeAccount)
.then(function cardCreated(){
res.send(200, {msg: 'Card created'});
})
.catch(function handleError(err) {
res.send(err.httpCode, err.msg);
})
},
For future, note that the () after the function name invokes the function, as order of execution in JS will evaluate it first due to being inside the then's ().
In promise chains, always invoke only the first function. Example:
function first () { /*...*/ } // All return promise.
function second() { /*...*/ }
function third () { /*...*/ }
first() // Invoked
.then(second) // Not invoked. second() will have been bad here.
.then(third);

Sequelize return response with nested loops

I am looping through and inserting elements in my addAll method. Once this has finished, I would like to return a response. My code currently works, but I am not sure how to get the response to happen.
Route
.post(function (req, res) {
var screening_module = Screening_module.build();
screening_module.screening_module = req.body.module;
screening_module.screening_module.organization_id = req.user.organization_id;
screening_module.addAll(function (success) {
res.json({message: 'screening_module created!'});
},
function (err) {
res.status(err).send(err);
}); });
AddAll
addAll: function (onSuccess, onError) {
var screening_module = this.screening_module;
screening_module.selectedUser.forEach(function (user) {
Screening_module.create({
organization_id: screening_module.organization_id,
supervisor: screening_module.supervisor.id,
name: screening_module.name,
description: screening_module.description,
deadline: screening_module.deadline,
category_id: screening_module.category.id,
screening_module_type_id: screening_module.type.id,
user_id: user.id
}).then(function (createdScreeningModule) {
Screening.create({
user_id: createdScreeningModule.dataValues.user_id,
screening_module_id: createdScreeningModule[null]
}).then(function (createdScreening) {
screening_module.selectedSkillsets.forEach(function (skillset) {
Screening_has_skillset.create({
screening_id: createdScreening[null],
skillset_id: skillset.id
})
})
})
})
});
},
Promise is your friend here
var Promise = require("bluebird");
addAll: function (onSuccess, onError) {
var screening_module = this.screening_module;
Promise.each(screening_module.selectedUser, function (user) {
return Screening_module.create({
organization_id: screening_module.organization_id,
supervisor: screening_module.supervisor.id,
name: screening_module.name,
description: screening_module.description,
deadline: screening_module.deadline,
category_id: screening_module.category.id,
screening_module_type_id: screening_module.type.id,
user_id: user.id
}).then(function (createdScreeningModule) {
return Screening.create({
user_id: createdScreeningModule.dataValues.user_id,
screening_module_id: createdScreeningModule[null]
}).then(function (createdScreening) {
return Promise.each(screening_module.selectedSkillsets, function (skillset) {
return Screening_has_skillset.create({
screening_id: createdScreening[null],
skillset_id: skillset.id
});
});
});
});
}).then(onSuccess, onError);
},
Untested, but hopefully give you an idea of how to handle. IMO, this kind of deep nesting is a code smell, and you might need to consider the design again

Resources