promise execution with condition - javascript

I have a following promise function
function run(args) {
return new Promise(function(resolve, reject) { //logic });
}
I want to pass an argument to the promise function run and on the returned value I want to have some condition, if it satisfies I want to pass one set of argument to the promise and if it fails the other set of result and on the basis of the above promise result a last promise with the consolidated values from the above promise as argument.
run(a)
.then(() =>{
if (condition) {
run(b)
.then(() => {
return something
})
}
else
run(c)
.then(() => {
return something
})
})
.then(rows => {
use the something returned
})
Something like above
Is it possible. Also what if both the condition is not satisfied how to handle the error ?
or is there any better way to do it ?

That's two questions, really, but oh well...
Conditionally returning one of two different promises
Basically, in the first .then(), you have to return a promise. You do so by adding return before run(b) and run(c). This will make the second .then() pick up the value of something.
.then(() => {
if (condition) {
return run(b).then(() => {
return something;
});
} else {
return run(c).then(() => {
return something;
});
}
})
Error throwing and handling
Your scenario is a simple if-else. One of the two will always be fulfilled, so there is no "if no condition is satisfied". However, let us assume you have two conditions instead, and one must be satisfied in order for the entire thing to be considered fulfilled. All you need to do is throw an Error:
.then(() => {
if (condition1) {
return run(b);
} else if (condition2) {
return run(c);
}
throw new Error("Neither condition was true.");
})
.then(rows => {
// use the something returned
})
.catch(err => console.log(err));
The thrown Error object will be received as argument err by the final .catch().

There are a bunch of different ways to handle this, but here is one.
then((data) => {
let nextCall;
if ( data === "Something") {
nextCall = somePromiseFunction(data);
} else {
nextCall = someOtherPromiseFunction(data);
}
return nextCall;
})
.then((data) => { ... })

Related

How to avoid propagation when chaining Promise with Non-Promise calls

I want the following code not to call OK logic, nor reject the promise. Note, I've a mixture of promise and non-promise calls (which somehow still managed to stay thenable after returning a string from its non-promise step), I just want the promise to stay at pending status if p1 resolves to non-OK value.
const p1 = new Promise((resolve, reject) => {
resolve("NOT_OK_BUT_NOT_A_CATCH_NEITHER");
});
p1.then(result => {
if (result !== "OK") {
return "How do I avoid calling OK logic w/out rejecting?";
}
else {
return Promise.resolve("OK");
}
}).then(result => {
console.error("OK logic...");
});
You've got two options:
1) throw an error:
p1.then(result => {
if (result =='notOk') {
throw new Error('not ok');
} else {
return 'OK';
}
})
.then(r => {
// r will be 'OK'
})
.catch(e => {
// e will be an error with message 'not ok', if it threw
})
the second .then won't run, the .catch will.
2) decide what to do in the latter .then conditionally:
p1.then(result => {
if (result =='notOk') {
return 'not ok'
} else {
return 'OK';
}
})
.then(r => {
if (r === 'OK') {
// do stuff here for condition of OK
}
})
This works because the second .then takes as an argument whatever was returned by the previous .then (however if the previous .then returned a Promise, the second .then's argument will be whatever was asyncronously resolved)
Note: if you .catch a promise that errored, and you return THAT promise, the final promise WON'T have an error, because the .catch caught it.
The best approach is to not chain that way. Instead do this:
const p1 = new Promise((resolve, reject) => {
resolve("NOT_OK_BUT_NOT_A_CATCH_NEITHER");
});
p1.then(result => {
if (result !== "OK") {
return "How do I avoid calling OK logic w/out rejecting?";
} else {
return Promise.resolve("OK")
.then(result => {
console.error("OK logic...");
});
}
})
If you're writing a linear chain, that means you're saying that you want step by step execution, which isn't what you want in this case.
Alternatively, if your target platforms/build system support it, write it as an async function:
(async function() {
const result = await Promise.resolve "NOT_OK_BUT_NOT_A_CATCH_NEITHER");
if (result !== "OK") {
return "How do I avoid calling OK logic w/out rejecting?";
} else {
await Promise.resolve("OK");
console.error("OK logic...");
}
})();
Found a way, not sure how good is it but it works. The idea is to have it as promises all the way and just not resolve when not needed.
In my case it saves me a hassle with managing ignorable results without polluting result with the likes of processable flags.
const p1 = new Promise((resolve, reject) => {
resolve("NOT_OK_BUT_NOT_A_CATCH_NEITHER");
});
p1.then(result => {
return new Promise((resolve, reject) => {
if (result === "OK") {
resolve(result);
}
// OR do nothing
console.error("Just Do nothing");
});
}).then(result => {
console.error("OK logic...");
});

Am I chaining Promises correctly or committing a sin?

I have not worked with Javascript in a long time, so now promises are a new concept to me. I have some operations requiring more than one asynchronous call but which I want to treat as a transaction where steps do not execute if the step before failed. Currently I chain promises by nesting and I want to return a promise to the caller.
After reading the chaining section of Mozilla's Using Promises guide, I'm not sure if what I'm doing is correct or equivalent to the "callback pyramid of doom".
Is there a cleaner way to do this (besides chaining with a guard check in each then)? Am I right in my belief that in Mozilla's example it will execute each chained then even when there is an error?
myfunction(key) => {
return new Promise((outerResolve, outerReject) => {
return new Promise((resolve, reject) => {
let item = cache.get(key);
if (item) {
resolve(item);
} else {
//we didnt have the row cached, load it from store
chrome.storage.sync.get(key, function (result) {
chrome.runtime.lastError
? reject({ error: chrome.runtime.lastError.message })
: resolve(result);
});
}
}).then((resolve) => {
//Now the inner most item is resolved, we are working in the 'outer' shell
if (resolve.error) {
outerReject(resolve);
} else {
//No error, continue
new Promise((resolve, reject) => {
chrome.storage.sync.get(keyBasedOnPreviousData, function (result) {
chrome.runtime.lastError
? reject({ error: chrome.runtime.lastError.message })
: resolve(result);
});
}).then((resolve) => {
//finally return the result to the caller
if (resolve.error) {
outerReject(resolve);
} else {
outerResolve(resolve);
}
});
}
});
});
}
Subsequent then statements are not executed (until a catch) when an exception is thrown. Also, .then returns a Promise, so you don't need to create an additional, outer Promise.
Try this example:
var p = new Promise((resolve, reject) => {
console.log('first promise, resolves');
resolve();
})
.then(() => {
throw new Error('Something failed');
})
.then(() => {
console.log('then after the error');
return('result');
});
p.then(res => console.log('success: ' + res), err => console.log('error: ' + err));
You will not see "then after the error" in the console, because that happens after an exception is thrown. But if you comment the throw statement, you will get the result you expect in the Promise.
I am not sure I understand your example entirely, but I think it could be simplified like this:
myfunction(key) => {
return new Promise((resolve, reject) => {
let item = cache.get(key);
if (item) {
resolve(item);
} else {
//we didnt have the row cached, load it from store
chrome.storage.sync.get(key, function (result) {
chrome.runtime.lastError
? throw new Error(chrome.runtime.lastError.message)
: resolve(result);
});
}
}).then((previousData) => {
// keyBasedOnPreviousData is calculated based on previousData
chrome.storage.sync.get(keyBasedOnPreviousData, function (result) {
chrome.runtime.lastError
? throw new Error(chrome.runtime.lastError.message)
: return result;
});
});
}
It's a bit of a mess. This is my attempt at rewriting. A good thing to try to avoid is new Promise().
function chromeStorageGet(key) {
return new Promise( (res, rej) => {
chrome.storage.sync.get(key, result => {
if (chrome.runtime.lastError) {
rej(new Error(chrome.runtime.lastError.message))
} else {
res(result)
}
});
});
});
function myfunction(key) {
const item = cache.get(key) ? Promise.resolve(cache.get(key)) : chromeStorageGet(key);
return item.then( cacheResult => {
return chromeStorageGet(keyBasedOnPreviousData);
});
}
Why avoid new Promise()?
The reason for this is that you want to do every step with then(). If any error happened in any of the promises, every promise in the chain will fail and any subsequent then() will not get executed until there is a catch() handler.
Lots of promise based-code requires no error handlers, because promise-based functions always return promises and exceptions should flow all the back to the caller until there is something useful to be done with error handling.
Note that the exceptions to these 2 rules are in my chromeStorageGet function. A few notes here:
new Promise can be a quick and easy way to convert callback code to promise code.
It's usually a good idea to just create a little conversion layer for this callback-based code. If you need chrome.storage.sync in other places, maybe create a little utility that promisifies all its functions.
If there is only 1 'flow', you can just use a series of then() to complete the process, but sometimes you need to conditionally do other things. Just splitting up these complicated operations in a number of different functions can really help here.
But this:
const result = condition ? Promise.resolve() : Promise.reject();
Is almost always preferred to:
const result = new Promise( (res, rej) => {
if (condition) {
res();
} else {
rej();
}
}

Conditionally call a promise (or not), but return either result to another promise

having trouble trying to write the following code in a way that doesn't involve nested promises.
function trickyFunction(queryOptions, data) {
return new Promise((resolve, reject) => {
if (data) {
resolve(data);
} else {
// ... a bunch of conditions to check and/or modify queryOptions. These checks and mods
// are vital but only required if data is not passed in. ...
if (anErrorHappensHere) {
reject('Oh no, an error happened');
}
somePromise(queryOptions).then((result) => {
resolve(result);
});
}
}).then((result) => {
criticalOperation1(result);
// the code here is long and shouldn't be duplicated
});
}
I really don't like the .then() chain after somePromise since it's inside the new Promise, but I really don't see a way around it. If I take the conditional out of a promise, then I'd have to duplicate the criticalOperation1 code, which isn't an option here. The conditional checks in the else block should only happen if data is not passed in. Making other functions is not permitted in my case, and using async/await is also not permitted in my case.
Does anyone have any ideas? I've worked with Promises for a bit but this one is stumping me.
I would just avoid using the new Promise syntax in this case and just start the promise chain early
function trickyFunction(queryOptions, data) {
return Promise.resolve()
.then( () => {
if (data) {
return Promise.resolve(data);
} else {
// ... a bunch of conditions to check and/or modify queryOptions. These checks and mods
// are vital but only required if data is not passed in. ...
if (anErrorHappensHere) {
// Could also just throw here
return Promise.reject('Oh no, an error happened');
}
return somePromise(queryOptions);
}
})
.then((result) => {
criticalOperation1(result);
// the code here is long and shouldn't be duplicated
});
}
function trickyFunction(queryOptions, data) {
return new Promise((resolve, reject) => {
if (anErrorHappensHere) {
reject('Oh no, an error happened');
}
resolve({data, queryOptions});
}).then((obj) => {
if(obj.data){
return Promise.resolve(obj.data);
} else {
return somePromise(obj.queryOptions)
}
}).then((result) => criticalOperation1(result));
.catch((err)=> console.log(err));
}

How to properly use resolve and reject for promises

I've started to look at using Promises and have begun by putting together a simple function and calling it a few times. I need a sanity check around reject and resolve.
Is this the correct way to "promisify" a function?
Is this the correct way to deal with reject and resolve?
Anything I've got totally wrong?
const Redis = require('ioredis');
const redis = new Redis({
port: 6379,
host: '127.0.0.1'
});
function checkValues(name, section) {
return new Promise((resolve, reject) => {
redis.multi()
.sismember('names', name)
.sismember('sections', section)
.exec()
.then((results) => {
if(results[0][1] === 1 && results [1][1] ===1) {
reject('Match on both.');
} else if(results[0][1] === 1 || results [1][1] ===1) {
reject('Match on one.');
} else {
redis.multi()
.sadd('names', name)
.sadd('sections', section)
.exec()
.then((results) => {
// Lazy assumption of success.
resolve('Added as no matches.');
})
// No catch needed as this would be thrown up and caught?
}
})
.catch((error) => {
console.log(error);
});
});
}
// Call stuff.
checkValues('barry', 'green')
.then((result) => {
// Added as no matches "resolve" message from 'barry', 'green'
console.log(result);
retutn checkValues('steve', 'blue');
})
.then((result) => {
// Added as no matches "resolve" message from 'steve', 'blue'
retutn checkValues('steve', 'blue');
})
.then((result) => {
// Match on both "reject" message from 'steve', 'blue'
console.log(result);
})
.catch((error) => {
console.log(error);
});
No, this is kind of an anti-pattern. You already have a function that returns a promise so you don't need to wrap it in another promise, you can just return it. Remember that then() returns a promise that resolves to the return value of then. You can also return another promise from then. Usually this looks super clean, but in this case you need some logic in the then function, so it gets a little messy.
function checkValues(name, section) {
// Just return this Promise
return redis.multi()
.sismember('names', name)
.sismember('sections', section)
.exec()
.then((results) => {
if(results[0][1] === 1 && results [1][1] ===1) {
// Rejections will be caught down the line
return Promise.reject('Match on both.');
} else if(results[0][1] === 1 || results [1][1] ===1) {
return Promise.reject('Match on one.');
} else {
// You can return another Promise from then()
return redis.multi()
.sadd('names', name)
.sadd('sections', section)
.exec()
}
})
// You don't need to catch here - you can catch everything at the end of the chain
}
Several points:
Don't use the explicit-promise-construction-antipattern
As a general guide in purging the anti-pattern, after removing the new Promise() wrapper, change resolve statements to return and reject statements to throw new Error(...).
.catch() catches! If errors are to be observable/handleable by the caller, then either don't catch in checkValues() or catch and re-throw. Catching without re-throwing will cause the returned promise to settle on its success path, never its error path, which is great for error recovery but not always appropriate.
Suggest that all three cases, 'Match on both', 'Match on one' and 'Added as no matches', are really successes. Unless there's a particular reason for wanting 'Match on both' and 'Match on one' to be seen as error conditions, then return rather than reject/throw. That way, your call stuff chain will progress down its success path, .then().then().then(), regardless of expected outcome; only an unexpected error will go down the error path to be caught by the final .catch(). This isn't a general rule; very often, throwing is the right thing to do, but not here.
function checkValues(name, section) {
return redis.multi()
.sismember('names', name)
.sismember('sections', section)
.exec()
.then((results) => {
if(results[0][1] === 1 && results [1][1] === 1) {
return 'Match on both.';
} else if(results[0][1] === 1 || results [1][1] ===1) {
return 'Match on one.';
} else {
return redis.multi()
.sadd('names', name)
.sadd('sections', section)
.exec()
.then((results) => {
return 'Added as no matches.';
});
}
})
.catch((error) => {
console.log(error);
throw error;
});
}
// Call stuff.
checkValues('barry', 'green')
.then((result) => {
console.log(result); // expect 'Added as no matches'
return checkValues('steve', 'blue');
})
.then((result) => {
return checkValues('steve', 'blue'); // expect 'Added as no matches'
})
.then((result) => {
console.log(result); // expect 'Match on both'
})
.catch((error) => {
// only an unexpected error will land here
console.log(error);
});

Promise chain with an asynchronous operation not executing in order

I have found other people asking about this topic but I haven't been able to get my promise chain to execute in order.
Here is a basic reproduction of what is happening:
function firstMethod(){
dbHelper.executeQuery(queryParameters).then(result => {
if (result === whatIAmExpecting) {
return dbHelper.doDbOperation(secondQueryParameters)}
else {
throw new Error('An error occurred')
}})
.then(doFinalOperation())
.catch(error => {
})
}
In the above code doFinalOperation() is called before the then function after executeQuery() is called.
Here is the implementation of executeQuery():
function executeQuery(parameter) {
return new Promise((resolve, reject) => {
const queryToExecute = `SELECT * FROM parameter`
return mySqlConnection.query(queryToExecute).then((result) => {
resolve(result)
}).catch(error => {
reject(error)
})
})
And here is the implementation of of the mySqlConnection.query method:
function query(queryString){
return new Promise((resolve, reject) =>
{
initConnection()
connection.connect()
require('bluebird').promisifyAll(connection)
return connection.queryAsync(queryString).then(function(results) {
connection.end();
resolve(results)
}).catch(error => {
reject(error)
})
})
It seems like I have incorrectly implemented the executeQuery() method. The database operation in the mySqlConnection.query is asychronous and I can see that's where the chain of promises stops happening in the expected order.
My question in a nutshell: How do I make the my chain of promises execute in order, and how I do stop a then() method from being executed before the previous Promise has called resolve() or reject()?
Thanks in advance.
then expects a function, but you have accidentally executed it, instead of passing it. Change:
then(doFinalOperation())
with:
then(doFinalOperation)
Now it will be the promise implementation that invokes it (at the proper time), not "you".
If your function needs arguments to be passed, then you can either
(1) Use bind:
then(doFinalOperation.bind(null, parameterOne, parameterTwo, parameterThree))
(2) Use a function expression
then(_ => doFinalOperation(parameterOne, parameterTwo, parameterThree))
Both your .then() methods get called on the first async operation...
Should be something like this:
function firstMethod(){
dbHelper.executeQuery(queryParameters).then(expectedResult => {
if (expectedResult === whatIAmExpecting) {
return dbHelper.doDbOperation(secondQueryParameters)}
.then(doFinalOperation())
.catch(error => {
};
}
else {
throw new Error('An error occurred')
}})
.catch(error => {
});
}

Categories

Resources