I want to stop promise chain after it resolved via some conditions. Below code is might useful to understand what am I saying.
function update(id, data) {
return new Promise((resolve, reject) => {
let conn;
pool.get()
.then((db) => {
conn = db;
if(Object.keys(data).length === 0) {
return resolve({ updated: 0 });
}
else {
return generateHash(data.password);
}
})
.then((hash) => {
conn.query("UPDATE ... ", (err, queryResult) => {
if(err) {
throw err;
}
resolve({ updated: queryResult.affectedRows });
});
})
.catch((err) => { ... })
});
}
Note that pool.get() is promise wrapped API for getting connection pool from MySQL module that I made.
What I'm trying to do is updating user data. And for save server resources, I avoided to update if no data to update(Object.keys(data).length === 0).
When I tried this code, second then(updating db) is always happening even if no data to update!
I read this post, but it didn't worked. Why the promise chain wasn't stopped when I called "return resolve();"? And how to I stop it properly? I really like using Promises, but sometimes, this kind of things make me crazy. It will be very appreciate to help me this problem. Thanks!
P.S. I'm using node v6.2.2 anyway.
Why the promise chain wasn't stopped when I called "return resolve();"?
You've returned from the current then callback and fulfilled the outer promise. But that doesn't "stop" anything, then then chain still will continue by resolving with the return value of the callback.
And how to I stop it properly?
You need to put the then call inside the if to have the condition apply to it:
pool.get()
.then((db) => {
…
if (Object.keys(data).length === 0) {
…({ updated: 0 });
} else {
return generateHash(data.password)
.then((hash) => {
conn.query("UPDATE ... ", (err, queryResult) => {
…
});
})
}
})
.catch((err) => { ... })
And in any case, you should avoid the Promise constructor antipattern! You should only promisify the query method:
function query(conn, cmd) {
return new Promise((resolve, reject) => {
conn.query(cmd, (err, queryResult) => {
if (err) reject(err); // Don't throw!
else resolve(queryResult);
});
});
}
and then use that:
function update(id, data) {
return pool.get()
.then(conn => {
if (Object.keys(data).length === 0) {
conn.close(); // ???
return { updated: 0 };
} else {
return generateHash(data.password)
.then(hash => {
return query(conn, "UPDATE ... ")
}).then(queryResult => {
conn.close(); // ???
return { updated: queryResult.affectedRows };
}, err => {
…
conn.close(); // ???
});
}
});
}
Notice that it might not make sense to get a connection from the pool if you can know beforehand that no query will be made, so probably you should put the if on the top level:
function update(id, data) {
if (Object.keys(data).length === 0) {
return Promise.resolve({ updated: 0 });
} else {
return pool.get()
.then(conn => {
return generateHash(data.password)
.then(hash => {
return query(conn, "UPDATE ... ")
}).then(queryResult => {
conn.close(); // ???
return { updated: queryResult.affectedRows };
}, err => {
…
conn.close(); // ???
});
});
}
}
This would be a good situation to use an if statement:
function update(id, data) {
if (Object.keys(data).length === 0) {
return Promise.resolve({ updated: 0 });
}
let conn;
return pool.get()
.then((db) => {
conn = db;
return generateHash(data.password);
})
.then((hash) => {
return new Promise(function (resolve, reject) {
conn.query("UPDATE ... ", (err, queryResult) => {
if(err) {
reject(err);
}
resolve({ updated: queryResult.affectedRows });
});
});
})
.catch((err) => { ... })
}
Related
Im playing arround with promises and callbacks and wonder what is the correct way to write a function that returns a promise if no callback is passed.
My result looks like this, but im not sure if this is correct (in the meaning of anti pattern)
const mySuperFunction = function mySuperFunction(data, cb) {
let wrapper = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() >= 0.5) {
resolve(Date.now());
} else {
reject(new Error("Not today..."));
}
}, 100);
});
if (cb) {
wrapper.then((result) => {
cb(null, result);
}, (error) => {
cb(error);
});
} else {
return wrapper;
}
};
mySuperFunction().then((time) => {
console.log(time)
}).catch((err) => {
console.log(err);
});
mySuperFunction(null, (err, time) => {
console.log(err, time)
});
Its simple: create a function and wrap the "work" code in a promise.
If no callback is passed to my function, i return the wrapped promise. If a calback is passed, i wrap/call it from .then(...) and .catch(...)
Is this ok, or do i miss some special cases where this dosn't work ?
Is this ok, or do i miss some special cases where this dosn't work ?
Your code works, but it adds unuseful overhead. A promise consumes more memory than callback and it is slower than callbacks.
I would write something like
const { promisify } = require('util')
function mySuperFunctionCallback (data, cb) {
setTimeout(() => {
if (Math.random() >= 0.5) {
cb(Date.now())
} else {
cb(new Error('Not today...'))
}
}, 100)
}
const mySuperFunctionAsync = promisify(mySuperFunctionCallback)
function mySuperFunction (data, cb) {
if (cb) {
mySuperFunctionCallback(data, cb)
} else {
return mySuperFunctionAsync(data)
}
}
mySuperFunction().then((time) => {
console.log(time)
}).catch((err) => {
console.log(err)
})
mySuperFunction(null, (err, time) => {
console.log(err, time)
})
Let's say I have this 'pseudo'code
static async fetchAuthData () {
AsyncStorage.getItem('authtoken', (err, value) => AuthData.token = value)
.then( (value) => {
{ ...Ok token fetched... }
})
.catch( (err) => {
return Promise.reject('Some sort of error');
});
AsyncStorage.getItem('userid', (err, value) => AuthData.userid = parseInt(value))
.then( (value) => {
{ ...Ok userid fetched... }
})
.catch( (err) => {
return Promise.reject('Some sort of error');
});
if (token ok && userid ok ) {
return Promise.resolve('ok');
}
else {
return Promise.reject('Some sort of error');
}
}
I assume if (token ok && userid ok ) will not get executed until the previous two promises are resolved or rejected.
Am I right?.
Is there some possibility for if (token ok && userid ok ) get executed before I even get the token?.
This static method is called at the very beginning of my app and this is where I decide to go directly to the app or navigate to the auth flow.
The documentation doesn't seem to be clear about this.
Add await to the AsyncStorage functions to pause the execution till promise gets resolved and goes to next line
static async fetchAuthData () {
await AsyncStorage.getItem('authtoken', (err, value) => AuthData.token = value)
.then( (value) => {
{ ...Ok token fetched... }
})
.catch( (err) => {
return Promise.reject('Some sort of error');
});
await AsyncStorage.getItem('userid',(err, value) => AuthData.userid = parseInt(value))
.then( (value) => {
{ ...Ok userid fetched... }
})
.catch( (err) => {
return Promise.reject('Some sort of error');
});
if (token ok && userid ok ) {
return Promise.resolve('ok');
}
else {
return Promise.reject('Some sort of error');
}
}
as #Amadan said, It will get executed before you get the token
, you need to wait all the promises until they're resolve
static async fetchAuthData () {
try {
const [authtoken, userid] = await Promise.all([
() => AsyncStorage.getItem('authtoken'),
() => AsyncStorage.getItem('userid'),
]
if (token ok && userid ok ) {
return Promise.resolve('ok');
}
} catch(err) {
// handle exception
}
}
You can make use of async and await which makes code to execute in a synchronous way.
Check the below code which does the same.
static async fetchAuthData () {
try{
AuthData.token = await AsyncStorage.getItem('authtoken')
userId = await AsyncStorage.getItem('userid')
AuthData.userId = parseInt(userId)
return true;
}catch(err){
console.log(err)
return false
}
}
If any errors comes up it will be catched in catch block and return false from here.
static async fetchAuthData() {
await new Promise((resolve, reject) => {
AsyncStorage.getItem('authtoken', (err, value) => AuthData.token = value)
.then((value) => {
resolve('...Ok token fetched...')
})
.catch((err) => {
reject('Some sort of error');
});
})
await new Promise((resolve, reject) => {
AsyncStorage.getItem('userid', (err, value) => AuthData.userid = parseInt(value))
.then((value) => {
resolve('...Ok token fetched...')
})
.catch((err) => {
reject('Some sort of error');
});
})
if (token ok && userid ok) {
return Promise.resolve('ok');
} else {
return Promise.reject('Some sort of error');
}
}
Reading other posts here the consensus is the forEach should be synchronous and blocking.
However I must have done something strange in my code because it doesn't appear that way:
var noDupes = false; // should be true but force no inserts for now
console.log('forEach');
courses.forEach((course) =>
{
const promiseNoDupe = new Promise((resolve, reject) =>
{
dbo.collection("courses").findOne({ id: course.id }, (err, result) =>
{
if (err) throw err;
if (result) { console.log('dupe'); return reject('dupe'); }
console.log('nodupe');
resolve('nodupe');
});
});
noDupes &= promiseNoDupe.then(() =>
{
console.log('true promise');
return true;
}).catch(() =>
{
console.log('false promise');
return false;
});
});
console.log('End forEach');
if (noDupes)
{
console.log('Inserting many');
dbo.collection("courses").insertMany(courses, (err, result) =>
{
if (err) return res.status(400).send(error.details[0].message);
res.send(courses);
});
}
else
{
console.log('No Dupes allowed');
res.status(400).send('Inserting duplicate ID not Allowed!');
}
Console output:
forEach
End forEach
No Dupes allowed
nodupe
true promise
nodupe
true promise
The end forEach is executed before the promise is completed and before any of the internal processing is conducted! Subsequently the logic waiting on the promise is processing ahead of time.
I'm not sure what is going wrong but I'm trying to wait for the completion of all checks in the forEach before committing any new records.
Thanks to charlietfl for steering me towards map() and Promise.all().
Here is the working code:
var dupePromises = courses.map((course) =>
{
return new Promise((resolve, reject) =>
{
dbo.collection("courses").findOne({ id: course.id }, (err, result) =>
{
if (err) throw err;
if (result) return reject(false);
resolve(true);
});
}).then(() =>
{
return true;
}).catch(() =>
{
return false;
});
});
Promise.all(dupePromises).then((results) =>
{
if (results.every((isnotDupe) => { return isnotDupe /* == true */ }))
{
dbo.collection("courses").insertMany(courses, (err, result) =>
{
if (err) return res.status(400).send(error.details[0].message);
res.send(courses);
});
}
else{
res.status(400).send('Inserting duplicate ID not Allowed!');
}
});
}
I have two functions that return promise. The first one provide host value, and the second one use the host value to get IP address. I can see that the first function is running without any issue. But looks like the callback function side getHostIps is not executed at all. Not sure why it happens....what's wrong with my promise function?
my promise chain:
getHostedZoneId(dns)
.then(hostZoneId => {
getHostIps(dns, hostZoneId);
})
.then(hostIps => {
logger.Info(hostIps); //hostIps is undefined
})
.catch(err => logger.error(err));
getHostedZoneId:
var getHostedZoneId = function(dns) {
var params = {
DNSName: dns,
};
return new Promise((resolve, reject) => {
findHostZoneByDNS(params, function(err, data) {
if(err) {
reject(err);
}
else {
resolve(data);
}
});
});
}
getHostIps:
var getHostIps = function(dns, hostZoneId) {
var params = {
HostedZoneId: hostZoneId,
StartRecordName: dns,
};
return new Promise((resolve, reject) => {
findHostIps(params, function(err, data) {
//logger.info("get there");
if(err) {
reject(err);
}
else {
resolve(data);
}
});
});
}
I logged hostIps and err and data, all of them are defined. So I am sure that the callback function inside promise is not executed. But not sure how to fix it.
Any feedback is appreciated! Thanks!
You have to return the promise from your then statement to complete the chain.
getHostedZoneId(dns)
.then(hostZoneId => {
return getHostIps(dns, hostZoneId); // Add return
})
.then(hostIps => {
logger.Info(hostIps);
})
.catch(err => logger.error(err));
I would like to create a function that returns a promise and if something throws an error within, it returns promise reject.
function promiseFunc(options) {
return new Promise(() => {
return options;
});
}
function myfunc(options) {
return new Promise(() => {
if (!options) throw new Error("missing options");
return promiseFunc(options).then((result) => {
if (result.throwerr) throw new Error("thrown on purpose");
return result.value;
});
});
};
My test as follows:
const myfunc = require("./myfunc");
describe('myfunc', () => {
it('should fail without options', () => {
return myfunc()
.then((result) => { throw new Error(result) }, (err) => {
console.log("test #1 result:", err.message === "missing options");
});
});
it('should fail on options.throwerr', () => {
return myfunc({throwerr: true})
.then((result) => {}, (err) => {
console.log("test #2 result:", err.message === "thrown on purpose");
});
});
it('should return options.value', () => {
return myfunc({value: "some result", throwerr: false})
.then((result) => {
console.log("test #3 result:", result === "some result");
}, (err) => {});
});
});
The first test pass, but the second and third fails.
Log #2 does not even run, so I assumed the "throw on purpose" messes up something, therefore I created test #3, where I don't throw anything, but it still fails.
What am I missing?
Solution:
function promiseFunc(options) {
return new Promise(resolve => {
return resolve(options);
});
}
function myfunc(options) {
return new Promise((resolve, reject) => {
if (!options) throw new Error("missing options");
return promiseFunc(options).then(result => {
if (result.throwerr) throw new Error("thrown on purpose");
return resolve(result.value);
}).catch(err => {
return reject(err);
});
});
};
You forgot to pass a function with resolve and reject parameters, so your promises just don't work.
function promiseFunc(options) {
return new Promise(resolve => { // resolve function
resolve(options)
})
}
module.exports = function myfunc(options) {
return new Promise((resolve, reject) => { // since you may either resolve your promise or reject it, you need two params
if (!options) {
return reject(new Error("missing options"))
}
return promiseFunc(options).then(result => {
if (result.throwerr) {
return reject(new Error("thrown on purpose"))
}
resolve(result.value)
})
})
}
... and the test (mocha)
const assert = require('assert'),
myfunc = require("./myfunc")
describe('myfunc', () => {
it('should fail without options', done => { // mind the callback, promises are always async
myfunc()
.catch(err => {
assert(err.message === "missing options")
done() // <- called here
})
})
it('should fail on options.throwerr', done => {
myfunc({throwerr: true})
.catch(err => {
assert(err.message === "thrown on purpose")
done()
})
})
it('should return options.value', done => {
return myfunc({value: "some result", throwerr: false})
.then(result => {
assert(result === "some result")
done()
})
})
})
I would like to create a function that returns a promise and if something throws an error within, it returns promise reject.
This will do it ...
var q = require('q'); // In recent versions of node q is available by default and this line is not required
function iReturnAPromise(num) {
var def = q.defer();
if (typeof num=== 'number') {
try {
var value = 100 / num;
def.resolve(value);
} catch(e) {
def.reject("oops a division error - maybe you divided by zero");
}
} else {
def.reject("o no its not a number");
}
return def.promise;
}
PS this function was coded freehand and has not been tested - but this will work. Obviously try catch should be used sparingly.
PS I prefer the q library implementation of promise instead of the default node promise library - they take a very different approach. q dispenses with all the wrapping!
using the promise library u wanted ...
function iReturnAPromise(num) {
return new Promise(function(resolve, reject) {
if (typeof num === 'number') {
try {
var value = 100 / num;
resolve(value);
} catch (e) {
reject("oops a division error - maybe you divided by zero");
}
} else {
reject("o no its not a number");
}
})
}
iReturnAPromise(7).then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
// Unexpectedly this is not an error in node 5.6 because div by 0 is not an error operation anymore!
iReturnAPromise(0).then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
iReturnAPromise("fred").then(
function(response) {console.log("success", response)},
function(response) {console.log("failure", response)}
);
you can see why i prefer the q syntax :)