Implementation of simple request chain logic with promises - javascript

I'm trying to implement dummy emulation of following logic:
But I'm not sure if I fully understand best practices of how to do it.
The main point of this task is to avoid triggering of redundant catch blocks callbacks. IMO if 1st request failed then all following code should stop.
I mean: if 1st request was failed, then we do not make 2nd request and do not call catch block of 2nd request promise.
In a few words I'm looking for very clean and simple solution like following:
firstRequest()
.then(r => {
console.log('firstRequest success', r);
return secondRequest();
}, e => console.log('firstRequest fail', e))
.then(r => {
console.log('secondRequest success', r);
// Should I return something here? Why?
}, e => console.log('secondRequest fail', e));
I've written following implementation. It works as expected in case of both requests are succeeded, and if 2nd request fails. But it works wrong if 1st request is failed (as you can see both catch block are triggering). You can play around with isFirstSucceed and isSecondSucceed flags to check it.
var ms = 1000;
var isFirstSucceed = false;
var isSecondSucceed = true;
var getUsersId = () => new Promise((res, rej) => {
console.log('request getUsersId');
setTimeout(() => {
if (isFirstSucceed) {
return res([1,2,3,4,5]);
} else {
return rej(new Error());
}
}, ms);
});
var getUserById = () => new Promise((res, rej) => {
console.log('request getUserById');
setTimeout(() => {
if (isSecondSucceed) {
return res({name: 'John'});
} else {
return rej(new Error());
}
}, ms);
});
getUsersId()
.then(r => {
console.info('1st request succeed', r);
return getUserById();
}, e => {
console.error('1st request failed', e);
throw e;
})
.then(
r => console.info('2nd request succeed', r),
e => {
console.error('2nd request failed', e);
throw e;
});
I can move then of 2nd request to then of 1st request but it looks ugly.
var ms = 1000;
var isFirstSucceed = false;
var isSecondSucceed = true;
var getUsersId = () => new Promise((res, rej) => {
console.log('request getUsersId');
setTimeout(() => {
if (isFirstSucceed) {
return res([1,2,3,4,5]);
} else {
return rej(new Error());
}
}, ms);
});
var getUserById = () => new Promise((res, rej) => {
console.log('request getUserById');
setTimeout(() => {
if (isSecondSucceed) {
return res({name: 'John'});
} else {
return rej(new Error());
}
}, ms);
});
getUsersId()
.then(r => {
console.info('1st request succeed', r);
getUserById().then(
r => console.info('2nd request succeed', r),
e => {
console.error('2nd request failed', e);
throw e;
});
}, e => {
console.error('1st request failed', e);
throw e;
})
Questions:
How to implement described logic according to all promises best practices?
Is it possible to avoid throw e in every catch block?
Should I use es6 Promises? Or it is better to use some promises library?
Any other advices?

Your flow diagram is the logic you want to achieve, but it isn't quite how promises work. The issue is that there is no way to tell a promise chain to just "end" right here and don't call any other .then() or .catch() handlers later in the chain. If you get a reject in the chain and leave it rejected, it will call the next .catch() handler in the chain. If you handle the rejection locally and don't rethrow it, then it will call the next .then() handler in the chain. Neither of those options matches your logic diagram exactly.
So, you have to mentally change how you think about your logic diagram so that you can use a promise chain.
The simplest option (what is probably used for 90% of promise chains) is to just put one error handler at the end of the chain. Any error anywhere in the chain just skips to the single .catch() handler at the end of the chain. FYI, in most cases, I find the code more readable with .catch() than the 2nd argument to .then() so that's how I've shown it here
firstRequest().then(secondRequest).then(r => {
console.log('both requests successful');
}).catch(err => {
// An error from either request will show here
console.log(err);
});
When you provide a catch block and you don't either return a rejected promise or rethrow the error, then the promise infrastructure thinks you have "handled" the promise so the chain continues as resolved. If you rethrow the error, then the next catch block will fire and any intervening .then() handlers will be skipped.
You can make use of that to catch an error locally, do something (like log it) and then rethrow it to keep the promise chain as rejected.
firstRequest().catch(e => {
console.log('firstRequest fail', e));
e.logged = true;
throw e;
}).then(r => {
console.log('firstRequest success', r);
return secondRequest();
}).then(r => {
console.log('secondRequest success', r);
}).catch(e => {
if (!e.logged) {
console.log('secondRequest fail', e));
}
});
Or, a version that marks the error object with a debug message and then rethrows and can then only logs errors in one place:
firstRequest().catch(e => {
e.debugMsg = 'firstRequest fail';
throw e;
}).then(r => {
console.log('firstRequest success', r);
return secondRequest().catch(e => {
e.debugMsg = 'secondRequest fail';
throw e;
});
}).then(r => {
console.log('secondRequest success', r);
}).catch(e => {
console.log(e.debugMsg, e);
});
I've even had situations where a little helper function saved me some code and some visual complexity, particularly if there are a bunch of these in the chain:
function markErr(debugMsg) {
return function(e) {
// mark the error object and rethrow
e.debugMsg = debugMsg;
throw e;
}
}
firstRequest()
.catch(markErr('firstRequest fail'))
.then(r => {
console.log('firstRequest success', r);
return secondRequest().catch(markErr('secondRequest fail'));
}).then(r => {
console.log('secondRequest success', r);
}).catch(e => {
console.log(e.debugMsg, e);
});
Taking each of your questions individually:
How to implement described logic according to all promises best practices?
Described above. I'd say the simplest and best practice is the very first code block I show. If you need to make sure when you get to the final .catch() that you have a uniquely identifiable error so you know which step caused it, then modify the rejected error in each individual function to be unique so you can tell which it was from the one .catch() block at the end. If you can't modify those functions, then you can wrap them with a wrapper that catches and marks their error or you can do that inline with the markErr() type solution I showed. In most cases, you just need to know there was an error and not the exact step it occurred in so usually that isn't necessary for every step in the chain.
Is it possible to avoid throw e in every catch block?
That depends. If the error objects are already unique, then you can just use one .catch() at the end. If the error objects are not unique, but you need to know which exact step failed, then you have to either use a .catch() at each step so you can mark the error uniquely or you need to modify each function in the chain to have a unique error.
Should I use es6 Promises?
Yes. No better way I know of.
Or it is better to use some promises library?
I'm not aware of any features in a promise library that would make this simpler. This is really just about how you want to report errors and whether each step is defining a unique error or not. A promise library can't really do that for you.
Any other advice?
Keep learning more about how to mold promises into a solution for each individual problem.

IMO, you can use async/await... Still, with promises but is much cleaner to look at. Here is my sample approach on above logic.
function firstRequest() {
return new Promise((resolve, reject) => {
// add async function here
// and resolve("done")/reject("err")
});
}
function secondRequest() {
return new Promise((resolve, reject) => {
// add async function here
// and resolve("done")/reject("err")
});
}
async function startProgram() {
try {
await firstRequest();
await secondRequest();
} catch(err) {
console.log(err);
goToEndFn();
}
}
startProgram(); // start the program

https://github.com/xobotyi/await-of
$ npm i --save await-of
import of from "await-of";
async () => {
let [res1, err1] = await of(axios.get('some.uri/to/get11'));
let [res2, err2] = await of(axios.get('some.uri/to/get22'));
if (err1) {
console.log('err1', err1)
}
if (err2) {
console.log('err2', err2)
}
console.log('res1', res1)
console.log('res2', res2)
};

Async/await maybe?
async function foo() {
try {
const firstResult = await firstRequest();
const secondResult = await secondRequest();
} catch(e) {
// e = either first or second error
}
}
In this code an error on the first request transfers control to the catch block and the second request won't start
Should I use es6 Promises?
Probably yes, until you're pretty sure your code will be used in obsolete environments. They are already not so new and flashy

you do not need handle error for every promise
you need handle error only as common error
do like this:
var ms = 1000;
var isFirstSucceed = false;
var isSecondSucceed = true;
var getUsersId = () => new Promise((res, rej) => {
console.log('request getUsersId');
setTimeout(() => {
if (isFirstSucceed) {
return res([1,2,3,4,5]);
} else {
return rej();
}
}, ms);
});
var getUserById = () => new Promise((res, rej) => {
console.log('request getUserById');
setTimeout(() => {
if (isSecondSucceed) {
return res({name: 'John'});
} else {
return rej(new Error());
}
}, ms);
});
getUsersId()
.then(r => {
console.info('1st request succeed', r);
return getUserById();
})
.then(r => {
console.info('2st request succeed', r);
return;
})
.catch((e) => {
console.error('request failed', e);
throw new Error(e);
})

You can abuse duck-typing technique to stop promise chain with return { then: function() {} };. I modified your code just right after this line console.error('1st request failed', e);
var ms = 1000;
var isFirstSucceed = false;
var isSecondSucceed = true;
var getUsersId = () => new Promise((res, rej) => {
console.log('request getUsersId');
setTimeout(() => {
if (isFirstSucceed) {
return res([1,2,3,4,5]);
} else {
return rej(new Error());
}
}, ms);
});
var getUserById = () => new Promise((res, rej) => {
console.log('request getUserById');
setTimeout(() => {
if (isSecondSucceed) {
return res({name: 'John'});
} else {
return rej(new Error());
}
}, ms);
});
getUsersId()
.then(r => {
console.info('1st request succeed', r);
return getUserById();
}, e => {
console.error('1st request failed', e);
return { then: function() {} };
})
.then(
r => console.info('2nd request succeed', r),
e => {
console.error('2nd request failed', e);
throw e;
});

Related

Promise chain continues after rejection

I'm having trouble to properly catch an error/reject in a promise chain.
const p1 = () => {
return new Promise((resolve, reject) => {
console.log("P1");
resolve();
});
};
const p2 = () => {
return new Promise((resolve, reject) => {
console.log("P2");
reject();
});
};
const p3 = () => {
return new Promise((resolve, reject) => {
console.log("P3");
resolve();
});
};
p1().catch(() => {
console.log("Caught p1");
}).then(p2).catch(() => {
console.log("Caught p2");
}).then(p3).catch(() => {
console.log("Caught p3");
}).then(() => {
console.log("Final then");
});
When the promise is rejected, the following .then still gets executed. In my understanding, when in a promise chain an error/reject happened, the .then calls that follow it are not executed any more.
P1
P2
Caught p2
P3
Final then
The rejection gets caught correctly, but why is "P3" logged after the catch?
What am I doing wrong?
To clarify #evolutionxbox, this is my expected result:
Promise.resolve().then(() => {
console.log("resolve #1");
return Promise.reject();
}).then(() => {
console.log("resolve #2");
return Promise.resolve();
}).then(() => {
console.log("resolve #3");
return Promise.resolve();
}).then(() => {
console.log("Final end");
}).catch(() => {
console.log("Caught");
});
This code works exactly like it should. And I can't see a difference to my code, except that I declared the functions separately.
The code above stops no matter where the promise is rejected.
Here is a synchronous equivalent of your code:
const f1 = () => {
console.log("F1");
};
const f2 = () => {
console.log("F2");
throw new Error();
};
const f3 = () => {
console.log("F3");
};
try {
f1();
} catch {
console.log("Caught f1");
}
try {
f2();
} catch {
console.log("Caught f2");
}
try {
f3();
} catch {
console.log("Caught f3");
}
console.log("Final code");
As you can see, that gives a matching result. Hopefully, looking at the synchronous code you would not be surprised why. In a try..catch you are allowed to attempt recovery. The idea is that the catch will stop the error propagation and you can hopefully continue further. Or if you do want to stop, you still have to explicitly throw again, for example:
doCode();
try {
makeCoffee();
} catch(err) {
if (err instanceof IAmATeapotError) {
//attempt recovery
makeTea();
} else {
//unrecoverable - log and re-throw
console.error("Fatal coffee related issue encountered", err);
throw err;
}
}
doCode();
This is also the purpose Promise#catch() serves - so you can attempt recovery or at least act when there was a problem. The idea is that after the .catch() you might be able to continue:
const orderPizza = (topping) =>
new Promise((resolve, reject) => {
if (topping === "pepperoni")
reject(new Error("No pepperoni available"));
else
resolve(`${topping} pizza`);
});
const makeToast = () => "toast";
const eat = food => console.log(`eating some ${food}`);
async function main() {
await orderPizza("cheese")
.catch(makeToast)
.then(eat);
console.log("-----");
await orderPizza("pepperoni")
.catch(makeToast)
.then(eat);
}
main();
In order to reject the promise chain from a .catch() you need to do something similar as a normal catch and fail at the error recovery by inducing another error. You can throw or return a rejected promise to that effect.
This code works exactly like it should. And I can't see a difference to my code, except that I declared the functions separately.
The code above stops no matter where the promise is rejected.
The second piece of code you show fails entirely after a reject because there are no other .catch()-es that are successful. It is basically similar to this synchronous code:
try {
console.log("log #1");
throw new Error();
console.log("log #2");
console.log("log #3");
console.log("Final end");
} catch {
console.log("Caught");
}
Thus if you do not want to recover early, you can also skip the .catch() instead of inducing another error.
Try this.
const p1 = (arg) => {
// Promise returns data in the respected arguments
return new Promise((resolve, reject) => {
// Data to be accessed through first argument.
resolve(arg);
});
};
const p2 = (arg) => {
return new Promise((resolve, reject) => {
// Data to be accessed through second argument.
reject(arg);
});
}
p1('p1').then(resolve => {
console.log(resolve + ' is handled with the resolve argument. So it is accessed with .then()');
}) // Since reject isn't configured to pass any data we don't use .catch()
p2('p2').catch(reject => {
console.log(reject + ' is handled with the reject argument. So it is accessed with .catch()');
}) // Since resolve ins't configured to pass any data we don't use .then()
// You would normally configure a Promise to return a value on with resolve, and access it with .then() when it completes a task successfully.
// .catch() would then be chained on to the end of .then() to handle errors when a task cannot be completed.
// Here is an example.
const p3 = () => {
return new Promise((resolve, reject) => {
var condition = true;
if (condition === true) {
resolve('P3');
} else {
reject('Promise failed!');
}
});
};
p3('p3').then(resolve => {
console.log(resolve);
}).catch(reject => {
console.log(reject);
})
You do not do anything wrong.
In your code you call the first promise p1. Then you write p1.catch(...).then(...).then(...).then(...). This is a chain which means that you should call then 3 times, because you called resolve method in the p1 promise (all these thens depend on the first promise).
When the promise is rejected, the following .then still gets executed.
Yes. Just to be accurate: the then and catch method calls are all executed synchronously (in one go), and so all promises involved are created in one go. It's the callbacks passed to these methods that execute asynchronously, as the relevant promises resolve (fullfill or reject).
In my understanding, when in a promise chain an error/reject happened, the .then calls that follow it are not executed any more.
This is not the case. The promise that a catch returns can either fullfill or reject depending on what happens in the callback passed to it, and so the callbacks further down the chain will execute accordingly when that promise resolves.
The rejection gets caught correctly, but why is "P3" logged after the catch?
As in your case the catch callback returns undefined (it only performs a console.log), its promise fullfulls! By consequence, the chained then callback -- on that promise -- is executed... etc.
If you want to "stop"
If you want to keep the chain as it is, but wish to have a behaviour where a rejection leads to no further execution of then or catch callbacks, then don't resolve the associated promise:
const stop = new Promise(resolve => null);
const p1 = () => {
return new Promise((resolve, reject) => {
console.log("P1");
resolve();
});
};
const p2 = () => {
return new Promise((resolve, reject) => {
console.log("P2");
reject();
});
};
const p3 = () => {
return new Promise((resolve, reject) => {
console.log("P3");
resolve();
});
};
p1().catch(() => {
console.log("Caught p1");
return stop; // don't resolve
}).then(p2).catch(() => {
console.log("Caught p2");
return stop;
}).then(p3).catch(() => {
console.log("Caught p3");
return stop;
}).then(() => {
console.log("Final then");
});

Why is my Promise always hitting the catch() method?

I'm using the JavaScript Promise object with a then(), catch().
The console.log in the catch() method always runs, regardless of the response from the API ("STATUS_SUCCESS" or "STATUS_FAILED").
Is this normal behaviour in promises or is there a way to only hit the catch() method if the response has failed?
Updated with live example:
sendAccountDataToBackend(response) {
const { formData } = response;
const requestObj = {
url: 'http://localhost:3000/api/validate',
data: {
firstname: 'dummy_firstname',
lastname: 'dummy_lastname',
email: 'dummyemail'
}
};
let p = new Promise((resolve, reject) => {
account.Utils.globalAjaxRequest(requestObj, (success) => {
if(success.status === 'STATUS_SUCCESS') {
resolve();
console.log('resolved: ', p)
} else {
reject();
console.log('rejected: ', p);
}
});
})
p.then(() => {
console.log('Then: ', response);
}).catch(() => {
console.log('catch:', response);
})
}
You can find the exact cause of the thrown error by printing it.
Change your catch handler to look like this:
catch((e) => {
console.log('Catch', e);
})
In addition to "Catch" you will see a description of the error in the console.
I figured out what was causing the catch to fire thanks to #pfcodes suggestion. I was calling a function within the then() block which was failing. Once removed, it stayed inside then(). Silly mistake that was over looked! Thanks for your suggestions.
Regarding the question:
Is this normal behaviour in promises or is there a way to only hit the catch() method if the response has failed?
It is easy to demonstrate that the catch is only entered when the reject call is made.
function testPromise(shouldReject)
{
return new Promise((resolve, reject) => {
setTimeout(function(){
if(shouldReject) reject();
else resolve();
},1000);
});
}
function handlePromise(p, name){
p.then(() => {
console.log(name, 'Then');
}).catch(() => {
console.log(name, 'Catch');
})
}
var rejectPromise = testPromise(true);
var resolvePromise = testPromise(false);
handlePromise(rejectPromise,"reject");
handlePromise(resolvePromise,"resolve");
So, what this means is that in your code, for whatever reason (probably badly handled asynchronous ajax call) you're entering the else block.
Regarding your update, I would say that success.status is returning something other than "STATUS_SUCCESS" for the reasons demonstrated by the code above.
Try adding console.log(success) inside the ajax callback.
account.Utils.globalAjaxRequest(requestObj, (success) => {
console.log(success);
....

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

How do I fix a race condition in a nested Promise in Node.js? [duplicate]

I'm creating an API using Node.js/TypeScript running Express. Below is an excerpt from my get method. An error is being triggered in the format method, which throws an error that is caught by the promise, but not propagated to the parent promise after a throw:
this.getModel(objectName).findAll(queryParameters).then(function(databaseObjects) {
for (let databaseObject of databaseObjects) {
var jsonObject = {};
//console.log("Database object: ");
//console.log(databaseObject);
transform.baseFormat(databaseObject, jsonObject)
.then(() => transform.format(databaseObject, jsonObject))
.then(() => {
res.locals.retval.addData(jsonObject);
}).catch((e) => {
console.log("Caught error during format of existing object: ");
console.log(e);
throw e;
});
}
})
.then(() => {
if (metadata) {
this.metadata(objectName, false, transform, res.locals.retval);
delete queryParameters.limit;
delete queryParameters.offset;
console.log("RUNNING METADATA COUNT: ");
this.getModel(objectName).count(queryParameters).then(function(count) {
res.locals.retval.setMetadata("records", count);
return next();
}).catch(function(e) {
this.error(e, res);
return next();
});
} else {
console.log("NO METADATA");
return next();
}
})
.catch((e) => {
// TODO: Move status into error() function
console.log("500 Error on GET");
console.error(e);
res.locals.retval.addError(ErrorCode.InternalError, e);
res.status(ErrorCode.InternalError).send(res.locals.retval);
return next();
});
Here's the output:
(node:8277) Warning: a promise was created in a handler at /Library/WebServer/adstudio/dist/server.js:555:51 but was not returned from it, see
at Function.Promise.bind (/Library/WebServer/adstudio/node_modules/bluebird/js/release/bind.js:65:20)
Caught error during format of existing object:
Test Error
END FUNCTION HAS BEEN REACHED!
Then the request fails to finish.
I've read a lot on Promises and I haven't been able to find an issue/solution similar to mine.
http://bluebirdjs.com/docs/warning-explanations.html
http://taoofcode.net/promise-anti-patterns/
https://www.reddit.com/r/javascript/comments/4bj6sm/am_i_wrong_to_be_annoyed_with_promise_error/
https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
Chained promises not passing on rejection
http://wiki.commonjs.org/wiki/Promises/A
https://promisesaplus.com/
Running inside that for-loop is not asynchronous, so your promise is resolving basically as soon as the loop finishes, yet before all your formatting finishes.
Use a promise control flow, like bluebird's Promise.each which is serial or just Promise.all. Then any exceptions will be caught.
this.getModel(objectName).findAll(queryParameters).then(function (databaseObjects) {
var promises = databaseObjects.map(databaseObject => {
var jsonObject = {}
// console.log("Database object: ");
// console.log(databaseObject);
return transform.baseFormat(databaseObject, jsonObject)
.then(() => transform.format(databaseObject, jsonObject))
.then(() => {
res.locals.retval.addData(jsonObject)
}).catch((e) => {
console.log('Caught error during format of existing object: ')
console.log(e)
throw e
})
})
return Promise.all(promises)
})
.catch((e) => {
// TODO: Move status into error() function
console.log('500 Error on GET')
console.error(e)
res.locals.retval.addError(ErrorCode.InternalError, e)
res.status(ErrorCode.InternalError).send(res.locals.retval)
return next()
})

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