How promise works with nested function calls - javascript

I have a piece of code which deals with user's data. There are bunch of nested function calls :
f1(){
f2(){
....
fn{
///
}
}
}
fn accesses a database which means it's asynchronous, so I wrote it somehow that it returns a promise and in fn-1 (the function which calls fn) , we use .then() to wait for this promise. But it looks like now I have to return a promise in fn-1 and so on. Is that true ?
var keyValueExists = function(key, value) {
var query = {};
query[key] = value;
return new Promise(function(resolve, reject) {
User.count(query, function(err, count) {
if (err) {
console.log(err);
console.log('Problem with `.find` function');
reject('Problem with `.find` function');
} else {
resolve(count !== 0);
}
});
});
};
var addUser = function(newUserInfo) {
var validationResult = Common._validateUserInfo(newUserInfo);
if (validationResult.isOK) {
keyValueExists('userName', newUserInfo.userName).then(function(userNameAlreadyExists) {
if (userNameAlreadyExists) {
validationResult = {
isOK: false,
reason: 'Username already exists',
infoWithBadInput: 'userName'
}
} else {
var newUserId = generateUserId();
//TODO: change it somehting more flexible. e.g. a predefined list of attributes to iterate over
var newUser = {
'userName': newUserInfo.userName,
'password': newUserInfo.password,
'userId': newUserId,
'lastModificationTime': Common.getCurrentFormanttedTime(),
'createdTime': Common.getCurrentFormanttedTime()
};
var user = new User(newUser);
user.save(function(err) {
if (err) {
console.log(err);
console.log('There is a problem saving the user info');
} else {
console.log('A new user added: ');
console.log(newUser);
}
});
}
return validationResult;
});
} else {
return validationResult;
}
};
addUser returns undefined ! It looks like that the caller of addUser doesn't wait for it !

This is what you are effectively doing in your addUser function
var addUser = function(newUserInfo) {
var validationResult = Common._validateUserInfo(newUserInfo);
if (validationResult.isOK) {
// ... do something asynchronously without returning anything
} else {
return validationResult;
}
}
So, yeah, if validationResult.isOK, adduser WILL return undefined
Here's some code loosely based on your code, but it runs standalone to demonstrate how you possibly should be doing things
var keyValueExists = function(key, value) {
// pseudo junk, this simulates any username starting with b as existing
return new Promise(function(resolve, reject) {
resolve(value.substr(0,1) == 'b'); // barny and betty are dupes, fred and wilma are not
});
}
var addUser = function (newUserInfo) {
// var validationResult = Common._validateUserInfo(newUserInfo);
var validationResult = {isOK: !!~(['fred', 'barny'].indexOf(newUserInfo.userName)), username: newUserInfo.userName}; // dummy code
if (validationResult.isOK) {
return keyValueExists('userName', newUserInfo.userName).then(function (userNameAlreadyExists) {
if (userNameAlreadyExists) {
validationResult = {
isOK: false,
reason: 'Username already exists',
infoWithBadInput: 'userName',
username: newUserInfo.userName
}
} else {
// create new user here
validationResult.userNumber = (Math.random() * 100000000) | 0;
}
return validationResult;
});
}
else {
// this function always needs to return a promise, even if it is resolved/rejected immediately
return Promise.reject(validationResult);
}
}
addUser({userName: 'fred'}).then(function (result) {
console.log(result);
}).catch(function(err) {
console.error(err);
});
addUser({userName: 'wilma'}).then(function (result) {
console.log(result);
}).catch(function(err) {
console.error(err);
});
addUser({userName: 'barny'}).then(function (result) {
console.log(result);
}).catch(function(err) {
console.error(err);
});
addUser({userName: 'betty'}).then(function (result) {
console.log(result);
}).catch(function(err) {
console.error(err);
});

Related

My arr is empty when I return it with a function

I'm trying to pass arrEntree in my return, however it's inside a promise. How would I fix this issue? The function query iniaites a new promise but like everytime ArrEntree is printed empty, because it gets called before the promise is finished processing. How would I finish the promise then only call my return...? How would I solve this issue.
function query(statement, params, first) {
return new Promise(function(resolve, reject) {
connection.query(statement, (params || []), function(err, rows) {
if (err) {
console.log("err", err);
if (err.code === "ETIMEDOUT") {
connectToDb();
return query(statement, params, first);
}
reject(err);
} else {
if (first) {
resolve(rows[0]);
} else {
resolve(rows);
}
}
});
});
}
function sendPlanEmails(){
if (plans.length === 0) {
return true;
}
const orders = plans.map(plan=>{
const arrEntree = []
const planData = srcData.plans[plan.productId];
const entrees = plan.entrees;
const entreeID = Object.keys(plan.entrees);
query('select * from entrees where entree_id in (' + entreeID.join(',') + ')')
.then(function(results){
results.forEach(function(element){
entreeID.forEach(function(second_elem){
if(element.entree_id==second_elem){
console.log('worked until here')
const quantity = plan.entrees[second_elem];
const name = element.entree;
arrEntree.push({name:name, quantity:quantity});
}
})
})
})
return {
days:plan.days.days,
planDataTitle:planData.title,
breakfast:plan.meals[0],
lunch:plan.meals[1],
dinner:plan.meals[2],
entreeArry:arrEntree
};
});
const template_mp = srcData.orderPage.mailchimp_template_mp || 'order_confirmation';
return utils.sendMealPlanEmail(template_mp, {
from: from,
order: orders,
});
}

How to ensure the chain of promises return an object

I have this function that has a chain of promises and I want to return an object:
var signIn = function(fb_ID,fb_accessToken) {
console.log("signin!")
var promise = new Parse.Promise();
var TokenStorage = Parse.Object.extend("tokenStorage");
var query = new Parse.Query(TokenStorage);
query.equalTo('facebookID', fb_ID);
query.ascending('createdAt');
var password;
var username;
var output = {};
var user;
// Check if this ID has previously logged in, using the master key
return query.first({ useMasterKey: true }).then(function(tokenData) {
// Update the accessToken if it is different.
if (fb_accessToken !== tokenData.get('fb_accessToken')) {
console.log('1')
user = tokenData.get('user');
user.fetch({
success: function(data) {
username = data.get('username')
tokenData.set('fb_accessToken', fb_accessToken);
/*
password = new Buffer(24);
_.times(24, function(i) {
password.set(i, _.random(0, 255));
password = password.toString('base64')
})
*/
password = (Date.now().toString(36) + Math.random().toString(36).substr(2, 10)).toUpperCase();
user.setPassword(password);
tokenData.set('password',password)
console.log('fetch')
return
}
}).then(function(){
tokenData.save(null, { useMasterKey: true }).then(function(tokenuser) {
console.log('tokensave')
user.save();
return Parse.User.logIn(username, password);
}).then(function(data) {
// Return the user object.
console.log('output return')
output.success = true
output.isnewuser = false
output.username = username;
output.password = password;
output.fb_accessToken = fb_accessToken;
//return Parse.Promise.as(output);
//promise.resolve(output)
//return promise
return output
});
})
} else {
console.log('2')
Parse.Promise.as().then(function() {
username = tokenData.get('username');
password = tokenData.get('password');
return
}).then(function(){
return Parse.User.logIn(username, password)
.then(function(data) {
// Return the user object
output.success = true
output.isnewuser = false
output.username = username;
output.password = password;
output.fb_accessToken = fb_accessToken;
console.log('oo'+JSON.stringify(output))
//return Parse.Promise.as(output);
//promise.resolve(output)
return output
});
})
}
})
//return promise
}
The function has two if statements and I would like that both will return 'output' object when I call:
signIn(fb_ID,fb_accessToken).then(function(data){
if (data.success == true) {
console.log('inn'+JSON.stringify(data))
response.success(data)
}
else {
console.log('errr')
response.error(data)
}
})
The 'data' object should be the 'output' object inside the SignIn function. Any idea?
You should put return before user.fetch and tokenData.save in first if block. In the else block put return before Parse.promise.as.
Your current implementation looks like something similar to this.
var someFunction = function () {
return mainPromise().then(function () {
if (someCondition) {
somePromiseOne().then(function () {
return data;
});
}
else {
somePromiseTwo().then(function () {
return data;
});
}
});
};
In the both cases you should put return before somePromiseOne and somePromiseTwo in order to pass the data to the promise chain.
You can do quite a lot to tidy things up ... and get it working.
First, you can (and should) move the code from the user.fetch({ success:... }) callback into the following then's callback. "Property-style" callbacks should be avoided when a promise interface is available (and already used).
Then, you might aim for flow control as follows :
doSomethingAsync().then(function() {
var promise;
if(condition) {
promise = ...; // deliver username and password
} else {
promise = ...; // deliver username and password
}
return promise.then(function(details) {
return login().then(function() {
// fully compose output object here
return details;
});
});
});
In practice, the code will be more extensive due to the number of async steps and the volume of synchronous code, particularly in the first condition.
In full, you might end up with something like this :
var signIn = function(fb_ID, fb_accessToken) {
var query = new Parse.Query(Parse.Object.extend('tokenStorage'));
query.equalTo('facebookID', fb_ID);
query.ascending('createdAt');
// Check if this ID has previously logged in, using the master key
return query.first({ useMasterKey: true }).then(function(tokenData) {
var promise;
// Update the accessToken if it is different.
if (fb_accessToken !== tokenData.get('fb_accessToken')) {
var user = tokenData.get('user');
promise = user.fetch().then(function(data) {
var details = {
username: data.get('username'),
password: (Date.now().toString(36) + Math.random().toString(36).substr(2, 10)).toUpperCase()
};
tokenData.set('fb_accessToken', fb_accessToken);
tokenData.set('password', details.password);
user.setPassword(details.password);
// Assuming here that `tokenData.save()` and `user.save()` are both async and can be executed in parallel.
return Parse.Promise.when([
tokenData.save(null, { useMasterKey: true }),
user.save()
]).then(function() {
return details;
});
});
} else {
// here `promise` mimics the final successful state of the promise in the other condition.
// ie. promise-wrapped details
promise = Parse.Promise.as({
username: tokenData.get('username'),
password: tokenData.get('password')
});
}
return promise.then(function(details) {
// whichever condition was executed, username and password details are delivered here in an identical format.
return Parse.User.logIn(details.username, details.password).then(function() {
details.success = true;
details.isnewuser = false;
details.fb_accessToken = fb_accessToken;
return details; // deliver details to signIn's caller
});
});
});
}

keystone.js nested promise -> foreach -> list find scope issue

I am writing an service, where I retrieve a list of items from a another service, then iterate over result performing keystone.list operation(s).
I am loosing the return status in the find/exec operation. I have tried promises, async, etc.
If someone could point out the correct way to implement this, I would appreciate it.
general implementation:
exports = module.exports = function (req, res) {
var rtn = {
added: 0,
count: 0
}
service(params)
.then(function(svcResult) {
svcResult.forEach(function(item) {
rtn.count++; // <-- correctly seen in apiresponse
Artifact.model.find()
.where({ artifactId: item.id})
.exec(function(err, result) {
if (result.length == 0) {
result = new Artifact.model({
... populate from item ....
});
result.save();
rtn.added++; // <-- not seen in api response
});
});
res.apiResponse(rtn);
});
}
for starters, exec is an async call, which you are ignoring in your res.apiResponse and thus count is incremented and not added, to make life easy, I am moving the exec call outside and wrapping it with promise:
function pExec(id){
return new Promise(function(resolve, reject){
Artifact.model.find()
.where({ artifactId: id})
.exec(function(err, result){
console.log('result: ', result); // there is a possibility that this is not empty array, which seems to be the only case when you increment added value
err? reject(err): resolve(result);
});
});
}
exports = module.exports = function(req, res){ // I think it is 'exports' not 'exposts'
service(params)
.then(function(svcResult) {
var promises = svcResult.map(function(item){
rtn.count++;
return pExec(item.id).then(function(result){
if (result.length == 0) {
result = new Artifact.model({
//... populate from item ....
});
result.save(); // again this might be an async call whose response you might need before incrementing added...
rtn.added++; // <-- not seen in api response
};
});
});
Promise.all(promises).then(function(){
res.apiResponse(rtn);
});
});
}
Thanks... Here is what I have come up with so far....
function getArtifact(id) {
return new Promise(function (resolve, reject) {
Artifact.model.findOne()
.where({artifactId: id})
.exec(function (err, artifact) {
err ? resolve(null) : resolve(artifact);
});
});
}
function createArtifact(item) {
return new Promise(function (resolve, reject) {
var artifact = new Artifact.model({
// ... populate from item ....
});
artifact.save(function (err, artifact) {
err ? resolve(null) : resolve(artifact);
});
});
}
exports = module.exports = function (req, res) {
var rtn = {
success: false,
count: 0,
section: '',
globalLibrary: {
Added: 0,
Matched: 0
},
messages: [],
};
if (!req.user || !req.user._id) {
rtn.messages.push("Requires Authentication");
return res.apiResponse(rtn);
}
if (!req.params.section) {
rtn.messages.push("Invalid parameters");
return res.apiResponse(rtn);
}
var userId = req.user._id;
var section = req.params.section;
rtn.section = section;
service(section)
.then(function (svcResult) {
if (svcResult.length == 0 || svcResult.items.length == 0) {
rtn.messages.push("Retrieved empty collection");
return;
}
rtn.messages.push("Retrieved collection");
var artifacts = svcResult.items(function (item) {
rtn.count++;
return getArtifact(item.objectid)
.then(function (artifact) {
if (!artifact || artifact.length == 0) {
rtn.messages.push("Global Library Adding: " + item.name['$t']);
rtn.globalLibrary.Added++;
artifact = createArtifact(item);
} else {
rtn.globalLibrary.Matched++;
}
return artifact;
})
});
Promise.all(artifacts)
.then(function () {
rtn.success = true;
res.apiResponse(rtn);
});
});
}

Use ldapjs with promise

I want to convert the following code to use promise. It is working and output a user's attributes within the active directory.
var client = ldap.createClient({
url: ldap_url
});
client.bind(ldap_username, ldap_password, function (err) {
client.search(ldap_dn_search, opts, function (err, search) {
search.on('searchEntry', function (entry) {
var user = entry.object;
// It is working!!!. It outputs all user attributes.
console.log(user);
});
});
});
The following is my attempt, butit doesn't output anything.
var Promise = require('promise');
var client_bind = Promise.denodeify(client.bind);
var client_search = Promise.denodeify(client.search);
client_bind(ldap_username, ldap_password)
.then(function(err){
client_search(ldap_dn_search, opts)
.then(function(search){
var search_on = Promise.denodeify(search.on);
search_on('searchEntry')
.then(function(entry){
var user = entry.object;
// It doesn't output anything !!!
console.log(user);
});
});
});
I had the same problem.
Search emits events, so we need something that processes them and passes further along the chain.
Here is piece of code, that works for me:
var ldap = require('ldapjs');
var promise = require('bluebird');
var client = ldap.createClient({url: app.settings['ldap']['server']});
var uid;
promise.promisifyAll(client);
function searchPromise(res, notfoundtext) {
return new Promise(function(resolve, reject) {
var found = false;
res.on('searchEntry', function(entry) {
found = true;
resolve(entry);
});
res.on('error', function(e) {
reject(e.message);
});
res.on('end', function() {
if (!found) {
reject(notfoundtext);
}
});
});
}
client.searchAsync(app.settings['ldap']['baseDn'], {filter: '(mail='+credentials.email+')', scope: 'sub'})
.then(function(res) {
return searchPromise(res, 'User isn\'t exists.');
})
.then(function (entry) {
uid = entry.object.uid;
return client.bindAsync(entry.object.dn, credentials.password);
})
.then(function() {
return client.searchAsync('cn='+app.settings['ldap']['group']+',cn=groups,'+app.settings['ldap']['baseDn'], {scope: 'sub', filter: '(memberUid='+uid+')'});
})
.then(function(res) {
return searchPromise(res, 'User is not in group ' + app.settings['ldap']['group']);
})
.then(function() {
console.log('All is ok');
})
.catch(function(message) {
console.log('Error:' + message);
});
Immediately after the search I add one more step that catches the events, processes them, and passes it further along the chain. This makes the function searchPromise.
Good luck coding )
Most likely those methods do require to be called on client as a context, so you will need to bind() them before passing them to Promise.denodeify:
var client_bind = Promise.denodeify(client.bind.bind(client));
var client_search = Promise.denodeify(client.search.bind(client));
Also, a proper use of promises would look like this:
client_bind(ldap_username, ldap_password).then(function() {
return client_search(ldap_dn_search, opts);
// ^^^^^^ always return something from the callback
}).then(function(search) { // flatten your chain
return Promise.denodeify(search.on).call(search, 'searchEntry');
// ^^^^^^ an alternative to `bind`
}).then(function(entry){
var user = entry.object;
console.log(user);
}).catch(function(err) { // always catch errors!
console.error(err);
});
Using Bluebird Promises, the easy way to do this is to create your client normally, and then run the promisifyAll() on the client.
var ldap = require('ldapjs');
var Promise = require('bluebird');
var client = ldap.createClient({
url: 'ldap://my-server:1234',
});
Promise.promisifyAll(client);
Now you can call client.addAsync() and client.searchAsync() and such.
client.bindAsync(secUserDn, secUserPassword)
.then(doSearch) // if it works, call doSearch
.catch(function (err) { // if bind fails, handle it
console.error('Error on bind', err)
});
function doSearch(data) {
client.searchAsync('CN=A Test,OU=Users,DC=website,DC=com', options)
.then(function (data) { // Handle the search result processing
console.log('I got a result');
})
.catch(function (err) { // Catch potential errors and handle them
console.error('Error on search', err);
});
}
i had the same issue here but i solved it by adding promise and resolve the response without using bluebird, this is an exemple of my code :
async getLdapUser(username: any): Promise<any> {
let myPromise = new Promise<boolean>((resolve, reject) => {
console.log('ssssssssss', username);
const adSuffix = 'OU=xxxx,OU=xxxxx,DC=xxxxxxx,DC=xxxxxx';
const password = 'xxxxxxxxxxxxx';
// Create client and bind to AD
const client = ldap.createClient({
url: 'ldap://1.1.1.1:389',
});
// promise.promisifyAll(client);
let resp = false;
// console.log(client);
client.bind('userTest', password,(err: any) => {
console.log('RESP', resp);
if (err) {
console.log('Error in new connetion ' + err);
} else {
/*if connection is success then go for any operation*/
console.log('Success');
const searchOptions: {} = {
scope: 'sub',
filter: '(sAMAccountName=' + username + ')',
attributes: ['sAMAccountName'],
};
client.search(adSuffix, searchOptions, (err: any, res: any) => {
assert.ifError(err);
res.on('searchEntry', (entry: any) => {
resp = true;
});
res.on('error', (error: any) => {
console.log('err');
reject(error.message);
});
await res.on('end', (result: any) => {
resolve(resp);
});
});
}
});
});
return myPromise;
}

Javascript promise not executing in desired order

I have been trying to execute a number of tests for an API, for this example it required my account details to be updated. When I run the test the retrieveAccount call is sometimes run before my putRequest making the tests fail. What am I doing wrong?
var frisby = require('frisby');
var url = require('endpoints.js');
var auth = require('auth.js');
var oracledb = require('oracledb');
var dbConnect = require('dbconfig.js');
var myDetails = undefined;
var putRequest = function() {
frisby.create('Put update contact details - required values')
.put(url.myAccount, {
addressLine1: 'String',
addressTown: 'String',
addressCounty: 'String'
}, {json: true})
.expectStatus(200)
.expectHeaderContains('content-type', 'application/json')
.auth(auth.username, auth.password)
.toss();
}
var retrieveAccount = function() {
oracledb.getConnection(
{
user : dbConnect.user,
password : dbConnect.password,
connectString : dbConnect.connectString
},
function(err, connection)
{
if (err) {
console.error(err.message);
return;
}
connection.execute(
"SELECT addressLine1, addressTown, addressCounty "
+ "FROM accounts "
+ "WHERE account_id = 1",
function(err, result)
{
if (err) {
console.error(err.message);
return;
}
myDetails = JSON.stringify(result.rows);
myDetails = JSON.parse(myDetails);
});
});
}
var matchValues = function() {
frisby.create('Match Database and API Values')
.get(url.myAccount)
.expectStatus(200)
.expectHeaderContains('content-type', 'application/json')
.auth(auth.username1, auth.password1)
.afterJSON(function (body) {
expect(body.addressLine1).toMatch(myDetails[0][0])
expect(body.addressCounty).toMatch(myDetails[0][1])
expect(body.addressTown).toMatch(myDetails[0][0])
})
.toss();
}
function Promise(fn) {
var state = 'pending';
var value;
var deferred = null;
function resolve(newValue) {
value = newValue;
state = 'resolved';
if(deferred) {
handle(deferred);
}
}
function handle(handler) {
if(state === 'pending') {
deferred = handler;
return;
}
if(!handler.onResolved) {
handler.resolve(value);
return;
}
var ret = handler.onResolved(value);
handler.resolve(ret);
}
this.then = function(onResolved) {
return new Promise(function(resolve) {
handle({
onResolved: onResolved,
resolve: resolve
});
});
};
fn(resolve);
}
function sendRequest() {
return new Promise(function(resolve){
var value = putRequest();
resolve(value);
});
}
function readDatabase() {
return new Promise(function(resolve){
var value = retrieveAccount();
resolve(value);
});
}
function getAccount() {
return new Promise(function(resolve){
var value = matchValues();
resolve(value);
});
}
sendRequest()
.then(readDatabase)
.then(getAccount);
I recommend using sequenty instead of promises to execute synchronous REST calls.
sudo npm install sequenty
var sequenty = require('sequenty');
function f1(cb) // cb: callback by sequenty
{
frisby.create('Match Database and API Values')
.get(url.myAccount)
.expectStatus(200)
.expectHeaderContains('content-type', 'application/json')
.auth(auth.username1, auth.password1)
.afterJSON(function (body) {
expect(body.addressLine1).toMatch(myDetails[0][0])
expect(body.addressCounty).toMatch(myDetails[0][1])
expect(body.addressTown).toMatch(myDetails[0][0])
cb();
})
.toss();
}
function f2(cb)
{
frisby.create('Put update contact details - required values')
.put(url.myAccount, {
addressLine1: 'String',
addressTown: 'String',
addressCounty: 'String'
}, {json: true})
.expectStatus(200)
.expectHeaderContains('content-type', 'application/json')
.auth(auth.username, auth.password)
.toss();
}
sequenty.run([f1, f2]);

Categories