How to properly use resolve and reject for promises - javascript

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);
});

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...");
});

promise execution with condition

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) => { ... })

Unhandled promise rejection in Node.js

I'm trying to make a DELETE call and I'm implementing the function below. I understand that in a promise, there needs to be a "resolve" and a "reject" state, but I'm getting an unhandled promise rejection error:
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): [object Object]
I don't really like using conditional statements inside a promise because it gets messy, but what I'm trying to do here is to check if the organization is verified, and if it is, a delete operation should not occur and will reject.
function deleteOrg(id) {
return new Promise((resolve, reject) => {
// A helper function that returns an 'org' object
findById(id)
.then((orgObject) => {
if (orgObject.verified_at !== null) {
throw new Error(422, 'Unable to delete organization')
}
//Organization is not verified, so proceed to delete
new Organization().where({'id': id}).destroy()
.then(() => {
return resolve() //return 200 upon deletion
})
.catch((err) => {
return reject(new Error(500, 'Unable to delete organization'))
})
})
.catch((err) => {
const m = `Unable to delete organization: ${err.message}`
return reject(new Error(500, m))
})
})
}
I'm pretty sure I'm handling the rejection inside the if wrong.
as findById and .destroy return Promises, there is no need for the Promsie constructor
Your code is then simplified to
function deleteOrg(id) {
return findById(id)
.then((orgObject) => {
if (orgObject.verified_at !== null) {
throw new Error(422, 'Unable to delete organization')
}
//Organization is not verified, so proceed to delete
return new Organization().where({'id': id}).destroy()
.catch((err) => {
throw (new Error(500, `Unable to delete organization: ${err.message}`));
});
});
}
Creating promises inside promise constructors is a known anti-pattern. Try modularizing your promises into separate functions instead:
function deleteOrg(id) {
const verifyOrg = (orgObject) => {
if (orgObject.verified_at !== null) {
throw new Error(422, 'Unable to delete organization')
}
};
const destroyOrg = () => new Organization().where({
'id': id
}).destroy();
return findById(id)
.then(verifyOrg)
.then(destroyOrg);
}
Where you can let the errors propagate through the promise chain and handle them outside:
deleteOrg(id)
.catch((err) => {
const m = `Unable to delete organization: ${err.message}`;
// ...
});
original method() => {
try{ //code to raise the exceptio
})
;
The best way to handle is to use expect It can be matched with an exception. A sample test
someMock.mockFunc(() => {
throw new Error("Something");
});
test('MockFunc in error', () => {
return expect(orginalCall()).rejects.toMatch('Something');
});

TypeScript error TS2345 when rejecting a Promise with an error

I have a TypeScript error message whose error I do not understand. The error message is:
error TS2345: Argument of type '(error: Error) => void | Promise' is not assignable to parameter of type '(reason: any) => IdentityKeyPair | PromiseLike'.
Type 'void | Promise' is not assignable to type 'IdentityKeyPair | PromiseLike'.
My code was working fine but TypeScript got mad at me when I changed this block:
.catch((error) => {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
})
into this:
.catch((error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
return reject(error);
}
})
Here is the complete code which was working:
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch((error) => {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
})
.then((identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
return this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID);
})
.then((lastResortPreKey: Proteus.keys.PreKey) => {
return resolve(lastResortPreKey);
})
.catch(reject);
});
}
And here is the code which does not compile anymore:
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch((error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
return reject(error);
}
})
.then((identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
return this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID);
})
.then((lastResortPreKey: Proteus.keys.PreKey) => {
return resolve(lastResortPreKey);
})
.catch(reject);
});
}
Does anyone sees why the TypeScript compiler refuses my return reject(error); statement with error code TS2345?
Screenshot:
I am using TypeScript 2.1.4.
Try out below. When you are in a then or catch block you can return a Promise or a value which gets wrapped into a Promise. You are manually working with a Promise yourself so you can just call the resolve and reject handlers without needing to return anything. Returning reject(error) would try to take that returned value, wrap it in a Promise and then try to pass to the next then block which is why you were getting the error you did. Think of it this way: returning something in a handler means continue down the chain with this new value. In your case I think you just want to stop the chaining and have the Promise you are creating resolve or reject under certain conditions.
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch((error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
throw error;
}
})
.then((identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
return this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID);
})
.then((lastResortPreKey: Proteus.keys.PreKey) => {
resolve(lastResortPreKey);
})
.catch((error) => {
reject(error);
});
});
}
You can't stop a Promise chain (cancellation aside), not even by returnning reject(), which is a definite misuse of Promises (you're not supposed to wrap a Promise in another Promise constructor).
Let's start with what you could do, then go to what you should do.
You could let the rejection bubble down the Promise chain, rethrowing it when it doesn't match your type guard, and at the bottom of the line, after all the .catch() clauses exhausted themselves, the Promise returned from your function will reject.
Now
Think about how you would do it in sync code. You'd have something like this:
try {
try {
actionThatThrows();
} catch (err) {
breakEverything();
}
continue other steps
} catch(err) {
generalErrorHandling();
}
That kind of code is not OK, and it isn't OK in Promises either. You should move distinct actions into functions which can resolve or reject on their own, use Errors as they were meant, an exception that bubbles up the stack until it meets something that can handle it.
Also, and because you're using TS 2.1.x, for long async flows, an async function is recommended.
Your return is useless there, it's the end of the onRejection callback.
And the return reject() will fulfill the next .then() promise anyway.
However, if you throw the error, it'll be inherited in the following promises down to the .catch(reject);
Basically: in any catch/then, return will resolve the child promise, and throw will reject the child promise.
I rewrote your code for a better flow of the promise chain.
public init(): Promise<Array<Proteus.keys.PreKey>> {
return new Promise((resolve, reject) => {
this.store.load_identity()
.catch(
(error) => {
if (error instanceof RecordNotFoundError) {
let identity: Proteus.keys.IdentityKeyPair = Proteus.keys.IdentityKeyPair.new();
return this.store.save_identity(identity);
} else {
throw error;
}
}
)
.then(
(identity: Proteus.keys.IdentityKeyPair) => {
this.identity = identity;
resolve(this.store.load_prekey(Proteus.keys.PreKey.MAX_PREKEY_ID));
},
reject
)
});
}

JavaScript Promises - reject vs. throw

I have read several articles on this subject, but it is still not clear to me if there is a difference between Promise.reject vs. throwing an error. For example,
Using Promise.reject
return asyncIsPermitted()
.then(function(result) {
if (result === true) {
return true;
}
else {
return Promise.reject(new PermissionDenied());
}
});
Using throw
return asyncIsPermitted()
.then(function(result) {
if (result === true) {
return true;
}
else {
throw new PermissionDenied();
}
});
My preference is to use throw simply because it is shorter, but was wondering if there is any advantage of one over the other.
There is no advantage of using one vs the other, but, there is a specific case where throw won't work. However, those cases can be fixed.
Any time you are inside of a promise callback, you can use throw. However, if you're in any other asynchronous callback, you must use reject.
For example, this won't trigger the catch:
new Promise(function() {
setTimeout(function() {
throw 'or nah';
// return Promise.reject('or nah'); also won't work
}, 1000);
}).catch(function(e) {
console.log(e); // doesn't happen
});
Instead you're left with an unresolved promise and an uncaught exception. That is a case where you would want to instead use reject. However, you could fix this in two ways.
by using the original Promise's reject function inside the timeout:
new Promise(function(resolve, reject) {
setTimeout(function() {
reject('or nah');
}, 1000);
}).catch(function(e) {
console.log(e); // works!
});
by promisifying the timeout:
function timeout(duration) { // Thanks joews
return new Promise(function(resolve) {
setTimeout(resolve, duration);
});
}
timeout(1000).then(function() {
throw 'worky!';
// return Promise.reject('worky'); also works
}).catch(function(e) {
console.log(e); // 'worky!'
});
Another important fact is that reject() DOES NOT terminate control flow like a return statement does. In contrast throw does terminate control flow.
Example:
new Promise((resolve, reject) => {
throw "err";
console.log("NEVER REACHED");
})
.then(() => console.log("RESOLVED"))
.catch(() => console.log("REJECTED"));
vs
new Promise((resolve, reject) => {
reject(); // resolve() behaves similarly
console.log("ALWAYS REACHED"); // "REJECTED" will print AFTER this
})
.then(() => console.log("RESOLVED"))
.catch(() => console.log("REJECTED"));
Yes, the biggest difference is that reject is a callback function that gets carried out after the promise is rejected, whereas throw cannot be used asynchronously. If you chose to use reject, your code will continue to run normally in asynchronous fashion whereas throw will prioritize completing the resolver function (this function will run immediately).
An example I've seen that helped clarify the issue for me was that you could set a Timeout function with reject, for example:
new Promise((resolve, reject) => {
setTimeout(()=>{reject('err msg');console.log('finished')}, 1000);
return resolve('ret val')
})
.then((o) => console.log("RESOLVED", o))
.catch((o) => console.log("REJECTED", o));
The above could would not be possible to write with throw.
try{
new Promise((resolve, reject) => {
setTimeout(()=>{throw new Error('err msg')}, 1000);
return resolve('ret val')
})
.then((o) => console.log("RESOLVED", o))
.catch((o) => console.log("REJECTED", o));
}catch(o){
console.log("IGNORED", o)
}
In the OP's small example the difference in indistinguishable but when dealing with more complicated asynchronous concept the difference between the two can be drastic.
TLDR: A function is hard to use when it sometimes returns a promise and sometimes throws an exception. When writing an async function, prefer to signal failure by returning a rejected promise
Your particular example obfuscates some important distinctions between them:
Because you are error handling inside a promise chain, thrown exceptions get automatically converted to rejected promises. This may explain why they seem to be interchangeable - they are not.
Consider the situation below:
checkCredentials = () => {
let idToken = localStorage.getItem('some token');
if ( idToken ) {
return fetch(`https://someValidateEndpoint`, {
headers: {
Authorization: `Bearer ${idToken}`
}
})
} else {
throw new Error('No Token Found In Local Storage')
}
}
This would be an anti-pattern because you would then need to support both async and sync error cases. It might look something like:
try {
function onFulfilled() { ... do the rest of your logic }
function onRejected() { // handle async failure - like network timeout }
checkCredentials(x).then(onFulfilled, onRejected);
} catch (e) {
// Error('No Token Found In Local Storage')
// handle synchronous failure
}
Not good and here is exactly where Promise.reject ( available in the global scope ) comes to the rescue and effectively differentiates itself from throw. The refactor now becomes:
checkCredentials = () => {
let idToken = localStorage.getItem('some_token');
if (!idToken) {
return Promise.reject('No Token Found In Local Storage')
}
return fetch(`https://someValidateEndpoint`, {
headers: {
Authorization: `Bearer ${idToken}`
}
})
}
This now lets you use just one catch() for network failures and the synchronous error check for lack of tokens:
checkCredentials()
.catch((error) => if ( error == 'No Token' ) {
// do no token modal
} else if ( error === 400 ) {
// do not authorized modal. etc.
}
There's one difference — which shouldn't matter — that the other answers haven't touched on, so:
If the fulfillment handler passed to then throws, the promise returned by that call to then is rejected with what was thrown.
If it returns a rejected promise, the promise returned by the call to then is resolved to that promise (and will ultimately be rejected, since the promise it's resolved to is rejected), which may introduce one extra async "tick" (one more loop in the microtask queue, to put it in browser terms).
Any code that relies on that difference is fundamentally broken, though. :-) It shouldn't be that sensitive to the timing of the promise settlement.
Here's an example:
function usingThrow(val) {
return Promise.resolve(val)
.then(v => {
if (v !== 42) {
throw new Error(`${v} is not 42!`);
}
return v;
});
}
function usingReject(val) {
return Promise.resolve(val)
.then(v => {
if (v !== 42) {
return Promise.reject(new Error(`${v} is not 42!`));
}
return v;
});
}
// The rejection handler on this chain may be called **after** the
// rejection handler on the following chain
usingReject(1)
.then(v => console.log(v))
.catch(e => console.error("Error from usingReject:", e.message));
// The rejection handler on this chain may be called **before** the
// rejection handler on the preceding chain
usingThrow(2)
.then(v => console.log(v))
.catch(e => console.error("Error from usingThrow:", e.message));
If you run that, as of this writing you get:
Error from usingThrow: 2 is not 42!
Error from usingReject: 1 is not 42!
Note the order.
Compare that to the same chains but both using usingThrow:
function usingThrow(val) {
return Promise.resolve(val)
.then(v => {
if (v !== 42) {
throw new Error(`${v} is not 42!`);
}
return v;
});
}
usingThrow(1)
.then(v => console.log(v))
.catch(e => console.error("Error from usingThrow:", e.message));
usingThrow(2)
.then(v => console.log(v))
.catch(e => console.error("Error from usingThrow:", e.message));
which shows that the rejection handlers ran in the other order:
Error from usingThrow: 1 is not 42!
Error from usingThrow: 2 is not 42!
I said "may" above because there's been some work in other areas that removed this unnecessary extra tick in other similar situations if all of the promises involved are native promises (not just thenables). (Specifically: In an async function, return await x originally introduced an extra async tick vs. return x while being otherwise identical; ES2020 changed it so that if x is a native promise, the extra tick is removed where there is no other difference.)
Again, any code that's that sensitive to the timing of the settlement of a promise is already broken. So really it doesn't/shouldn't matter.
In practical terms, as other answers have mentioned:
As Kevin B pointed out, throw won't work if you're in a callback to some other function you've used within your fulfillment handler — this is the biggie
As lukyer pointed out, throw abruptly terminates the function, which can be useful (but you're using return in your example, which does the same thing)
As Vencator pointed out, you can't use throw in a conditional expression (? :), at least not for now
Other than that, it's mostly a matter of style/preference, so as with most of those, agree with your team what you'll do (or that you don't care either way), and be consistent.
An example to try out. Just change isVersionThrow to false to use reject instead of throw.
const isVersionThrow = true
class TestClass {
async testFunction () {
if (isVersionThrow) {
console.log('Throw version')
throw new Error('Fail!')
} else {
console.log('Reject version')
return new Promise((resolve, reject) => {
reject(new Error('Fail!'))
})
}
}
}
const test = async () => {
const test = new TestClass()
try {
var response = await test.testFunction()
return response
} catch (error) {
console.log('ERROR RETURNED')
throw error
}
}
test()
.then(result => {
console.log('result: ' + result)
})
.catch(error => {
console.log('error: ' + error)
})
The difference is ternary operator
You can use
return condition ? someData : Promise.reject(new Error('not OK'))
You can't use
return condition ? someData : throw new Error('not OK')

Categories

Resources