I'm using that code
var ibmdb = require('ibm_db')
function executeSql(sql)
{
return new Promise( (resolve,reject) => {
ibmdb.open("DRIVER={DB2};DATABASE=SITRAN;...",(err,conn) => {
if (err) reject(Error(err))
conn.query(sql,(err,rows) => {
if (err) reject(Error(err))
console.log('length:',rows.length)
conn.close(() => { console.log('done'); resolve(rows); })
})
})
})
}
executeSql(sql)
.then((result) => { console.log('result:',result.length) })
The db2 part is working, it gives 1045 rows but the code is working strangely
length: 0
length: 1045
done
result: 0
I don't understand why length is displayed twice, one with 0 and one with 1045. And also why the resolve(rows) seems not working.
conn.query() is an asynchronous function which might explain the double console.log() behavior. As far as the weird behavior with resolve(), try resolve(rows.fetchAllSync()) instead.
Related
I'm looking to write some code that chains together some promises. I have a condition that based on the result of one promise I either call the next function that returns a promise and continue chaining another few functions, or I do nothing (effectively end the promise chain).
I have the following three possible solutions, I kinda think they all are a bit messy though.
Here is my first approach, what I dislike here is the nested promises.
initalAsyncCall()
.then((shouldContinue) => {
if (shouldContinue) {
return nextStep()
.then(() => anotherStep())
}
})
.catch((error) => {
handleError(error);
})
Here is my second. This one seems a bit longer and possibly harder to read
const shouldContinuePromise = initialAsyncCall();
const nextStepPromise = shouldContinuePromise.then((shouldContinue) => {
if (shouldContinue) return nextStep();
});
Promise.all([shouldContinuePromise, nextStepPromise])
.spread((shouldContinue) => {
if (shouldContinue) return anotherStep();
})
.catch((error) => {
handleError(error);
});
And finally here is my last approach. What I don't like here is I am throwing an error when it's not really an error.
initalAsyncCall()
.then((shouldContinue) => {
if (!shouldContinue) throw new HaltException()
return nextStep();
})
.then(() => anotherStep())
.catch(HaltException, (ex) => {
// do nothing... maybe some logging
})
.catch((error) => {
handleError(error);
})
Your first approach seems fine, to avoid the nesting you can return the promise and add an extra then block for the nested part like this
initalAsyncCall()
.then((shouldContinue) => {
if (shouldContinue) {
return nextStep()
} else {
throw Error('skip next step')
}
})
.then(() => anotherStep())
.catch((error) => {
handleError(error);
})
If you don't like throwing an unnecessary error in the third approach, You could use async/await to have more control and get rid of the function scope/nesting problem, which is also recommended for the new nodejs versions due to better error stack traces.
try {
const shouldContinue = await initalAsyncCall()
if (shouldContinue) {
await nextStep()
await anotherStep()
// or await Promise.all([nextStep(), anotherStep()]) if they're not dependent
}
}
catch (error) {
handleError(error);
}
I'm currently trying to send 2 objects to the front .hbs front end. However I cant seem to work out how to do this because I'm using promises.
Currently, my thinking is i perform the sql query, the country and organisation name is extracted, and then each sent to a geocoding api, returned and then squashed together in the same promises. But i'm not sure how to extract these for the render function.
Node
//route for homepage
app.get('/', (req, res) => {
let sql = "SELECT org_name, country_name from places;
let query = conn.query(sql, (err, results) => {
if (err) throw err;
const geoPromise = param => new Promise((resolve, reject) => {
geo.geocode('mapbox.places', param, function(err, geoData) {
if (err) return reject(err);
if (geoData) {
resolve(geoData.features[0])
} else {
reject('No result found');
}
});
});
const promises = results.map(result =>
Promise.all([
geoPromise(result.country_name),
geoPromise(result.org_name)
]));
Promise.all(promises).then((geoLoc, geoBus) => {
res.render('layouts/layout', {
results: JSON.stringify(geoLoc),
businesses: JSON.stringify(geoBus)
});
});
});
});
Front end call
results1 = {{{results}}}
console.log(results1.length)
business1 = {{{businesses}}}
console.log(business1.length)
Wrap your geo.geocode into a Promise
const geoPromise = param => new Promise((resolve, reject) => {
geo.geocode('mapbox.places', param, function(err, geoData) {
if (err) return reject(err);
if (geoData) {
resolve(geoData.features[0])
} else {
reject('No result found');
}
});
});
Combine both calls to geo.geocode
const promises = results.map(result =>
Promise.all([
geoPromise(result.country_name),
geoPromise(result.org_name)
]));
Call them
Promise.all(promises).then(([geoLoc, geoBus]) => {
res.render('layouts/layout', {
results: JSON.stringify(geoLoc),
businesses: JSON.stringify(geoBus)
});
});
As MadWard's answer mentions, deconstructing the argument of the callback of Promise.all is necessary since everything will be in the first argument. Make sure you check out his post for more details
Something important to recall: you will never have more than one argument in a then() callback.
Now you may ask: in the case of Promise.all(), what is this value?
Well, it is an array with all the values from the promises it awaits, in the order in which they are called.
If you do:
Promise.all([
resolveVariable1, resolveVariable2, resolveVariable3
]).then((values) => {
})
values will be [variable1, variable2, variable3], the three variables that the promises resolve to.
Your case is, however, a bit more complicated. What is gonna be returned at the end is a 2-D array containing every entry. It is an array of length results.length, and each of its element has a length of 2. The first element is the result, and the second one is the business.
Here is your snippet:
Promise.all(promises)
.then((values) => {
let results = values.map(elmt => elmt[0]);
let businesses = values.map(elmt => elmt[1]);
res.render('layouts/layout', {
results: JSON.stringify(results),
businesses: JSON.stringify(businesses)
});
})
I was reading some nodejs tutorial which talks about rejection in nodejs. They say that it's best practice to reject an error instead of a string or an plain text. Taking example of this code.
This is a example of rejecting a string
function cookMeat(chef){
grillMeat(chef)
.then(meat => {
if(chef.isTired){
return Promise.reject(chef.tiredReason);
}
return Promise.resolve(meat);
})
}
function cookNoodle(cheif){
boilNoodle(chef)
.then(noodle => {
if(chef.isTired){
return Promise.reject(chef.tiredReason);
}
return Promise.resolve(noodle);
})
}
function cook(){
let chef
prepareFood()
.then(c => {
chef = c;
return true;
})
.then(() => cookMeat(chef))
.then(() => cookNoodle(chef))
.catch(err => {
state: Fail,
reason: error
})
.then(res => {
state:Ready
})
}
cook()
.then((res) => serveCustomer(res))
And this is a example of rejecting an error
function cookMeat(chef){
grillMeat(chef)
.then(meat => {
if(chef.isTired){
return Promise.reject(new Error(chef.tiredReason));
}
return Promise.resolve(meat);
})
}
function cookNoodle(cheif){
boilNoodle(chef)
.then(noodle => {
if(chef.isTired){
return Promise.reject(new Error(chef.tiredReason));
}
return Promise.resolve(noodle);
})
}
function cook(){
let chef
prepareFood()
.then(c => {
chef = c;
return true;
})
.then(() => cookMeat(chef))
.then(() => cookNoodle(chef))
.catch(err => {
state: Fail,
reason: error.message
})
.then(res => {
state:Ready
})
}
cook()
.then((res) => serveCustomer(res))
Since I want to use reject to skip part of the promise chain. So I am wondering if there are any difference?
wPromise rejections are similar to throwing exceptions / error objects.
There are two rules that apply to throwing exceptions that apply here too:
In javascript, it's better to throw an Error object. Among other things, you will get stack information. It's also what most people expect when using a javascript code base.
Don't use exceptions for flow-control
The second one is such a common advice, you can google it verbatim and learn more. You're using Promise rejections as flow control and this is a bad idea.
Your functions can be rewritten a bit though. This is even better:
function cookMeat(){
grillMeat()
.then(meat => {
if(meat.isRaw){
throw new Error(meat.rawReason);
}
return meat;
});
}
function cookNoodle(){
boilNoodle()
.then(noodle => {
if(noodle.isRaw){
throw new Error(noodle.rawReason);
}
return noodle;
})
}
function cook(){
return prepareFood()
.then(() => cookMeat())
.then(() => cookNoodle())
.catch(err => {
state: Fail,
reason: error.message
})
.then(res => {
state:Ready
})
}
cook()
.then((res) => talkWithCustomer(res))
I got rid of your Promise.reject and Promise.resolve statements, because they are unneccary from within a then() function. The advice to use them only really applies 'outside' of then() chains.
Hello,
I use Promise for a initialize node project.
I want to insert in my MongoDb the name of files on all of my branch on my git repository.
I use nodegit to manipulate repo, for every nodegit method the return is a Promise. But i need to loop on all branch reference for get all files and branch name.
After that i can prepare my array for insert in database on next promise.
Code look like :
// List all branchs
.then((branchs) => {
let promises = [];
let allBranchFiles = [];
branchs.map((branch) => {
let q = repo.checkoutBranch(branch, config.checkoutOpts)
.then(() => repo.getCurrentBranch())
.then((ref) => {
return new Promise((resolve, reject) => {
fs.readdir('./myRepo/', (err, files) => {
files.map((file) => {
allBranchFiles.push({fileName: file, branch: branch});
});
resolve(allBranchFiles);
});
});
});
promises.push(q);
});
return Promise.all(promises);
})
That code finish by two way :
First :
{ Error: the index is locked; this might be due to a concurrent or crashed process
at Error (native) errno: -14 }
I'm sure no other process use git in this repo!
Second :
All of my files get the same value for "branch" but they are on separate branchs.
Thank you for helping me guys !
Cya.
I try something and it's work but it's ugly.
I mean, I use a recursiv function for doing my stuff. I wait better for change that.
I let you know how I replace my code on the question.
// List all branchs
.then((branchs) => {
return new Promise((resolve, reject) => recursiv(branchs).then(() => resolve(allBranchFiles)));
})
And my function :
let recursiv = (branchs, index=0) => {
return new Promise((resolved, rejected) => {
repo.checkoutBranch(branchs[index], config.checkoutOpts)
.then(() => repo.getCurrentBranch())
.then((user) => {
return new Promise((resolve, reject) => {
fs.readdir('./myRepo/', (err, files) => {
files.map((file) => {
allBranchFiles.push({fileName: file, branch: branchs[index]});
});
resolve();
});
});
})
.done(() => {
if (index < branchs.length-1) resolved(recursiv(branchs, index+1));
else resolved();
});
});
};
I'm implementing my service using Node.js with AWS DynamoDB (aws-sdk).
It's unclear for me how to implement the following scenario with promises:
I get a request to modify an entity with specified attributes
I'm trying to find the entity in the DB (async call find)
If the entity not found then create one with initial state (async call createInitialStateObject)
Modify the entity (which was in the DB before or just created on step 3) according to specific rules (async call applyModifications)
This is my first attempt:
function scenario(params) {
find(params).then((data) => {
let objectExists = checkExistense(data);
if (!objectExists) {
createInitialStateObject(params).then((data) => {
console.log("Object created");
// OK
}).catch((err) => {
console.error("Object not created");
// exit and return error
});
}
applyModifications(params).then((data) => {
// OK, return data
}).catch((err) => {
// exit and return error
});
}).catch((err) => {
// exit and return error
});
}
But the flaw here is that creation could happen before the modification, it's not bound to happen one after another.
The other attempt works, but looks a bit weird. I create an empty promise to call in case the object already exists:
function scenario(params) {
find(params).then((data) => {
let conditionalPromise = new Promise((resolve) => {
resolve(null);
});
let objectExists = checkExistense(data);
if (!objectExists) {
conditionalPromise = createInitialStateObject(params);
}
conditionalPromise.then((data) => {
applyModifications(params).then((data) => {
// OK, return data
}).catch((err) => {
// exit and return error
});
}).catch((err) => {
// exit and return error
});
}).catch((err) => {
// exit and return error
});
}
How it should be implemented in a right way?
Creating 'empty' or sync. Promises isn't unusual. There is even a short way of doing that: Promise.resolve(value) creates and resolves a Promise immediately.
Besides that you should make use of proper chaining and stop nesting things so much. Once you are in a chain, you don't even need to resolve an empty promise as a return value of a non thenable object is interpreted as exactly this.
function scenario(params) {
return find(params)
.then(data => {
let objectExists = checkExistense(data);
if (!objectExists) {
return createInitialStateObject(params);
}
// if we return nothing (or null in your case) this will be the same as return Promise.resolve()
return null;
})
.then(data => applyModifications(params))
.then(data => console.log(data))
.catch(err => console.log(err));
// exit and return error
}