Promise.all doesn't catch errors - javascript

I'm struggling to handle errors with Promise.all:
return Promise.all([
this.pagination(),
this.prepareParams(this.params, this.requestData),
this.fetchRecipes(this.apiData, this.params)
])
.catch(e => console.log());
Firstly I've just tried to throw an exception in this.pagination(), it was even handled, but strangely enough the next Promises were despite executed.
pagination(): Promise<any> {
return new Promise(resolve => {
if(...) {
...
resolve();
} else {
...
throw "no more results";
}
}
Then I tried to use Promise.reject("no more results") instead, but the problem is that it ignores now somehow my catch
Uncaught (in promise): no more results
UPDATE
Just to clarify for others: My original intention was to prevent the execution of the next 2 Promises, if the first one was rejected. Mistakenly I assumed that Promise.all fulfills this requirement. Thanks to the answer of traktor53 I realised that chaining promises is a better solution.

Promise.all waits for all the promises in its argument array to be fulfilled before returning an array of results, but will reject the promise it returned as soon as any of the promises in the array become rejected ("fast rejection").
Promise.all calls then on each of the promises passed to obtain its fulfilled value or rejected reason. It does not attempt to abort other promises if one of them is rejected - there is no mechanism in Promise standards to cancel a promised operation.
If you want to continue execution of promise operations only after a preceding promise is successful, use chaining rather than promise.all, taking care that the this value is correct in promise call backs (e.g. by using arrow functions):
function objectMethod () {
return this.pagination()
.then( data=>this.prepareParams(this.params, this.requestData))
.then( data=>this.fetchRecipes(this.apiData, this.params));
}
// catch errors encountered during call:
someObject.someMethod().catch(e => console.log(e));

The callback parameter to the Promise constructor (MDN) takes two arguments, idomatically called resolve and reject, the latter of which you are missing. Instead of throwing the error, you must pass it to reject. Therefore:
pagination(): Promise<any> {
return new Promise((resolve, reject) => {
if(...) {
...
resolve();
} else {
...
reject("no more results");
}
}

Better to add
try - catch block when you call a function
else
pagination(): Promise<any> {
return new Promise((resolve, reject) => {
if(...) {
...
resolve();
} else {
...
reject("no more results");
}
}
In your function call.
pagination().then(err, value) {}

Related

Why can't try/catch handle error throw in Promise constructor

This code get's unhandledRejection error and I don't know why.
If the Error is thrown in try/catch, shouldn't it be caught by Catch Expression?
async function main () {
try {
await run(throwError)
} catch (error) {
console.log('main catch error', error);
}
}
async function run (callback) {
return new Promise(async resolve => {
await throwError()
});
}
async function throwError () {
throw new Error('custom error')
}
process.on('unhandledRejection', (reason, promise) => {
console.log('unhandledRejection - reason', reason, promise);
})
main()
It's not caught because you're passing an async function into new Promise. An error inside an async function rejects the promise that the function returns. The Promise constructor doesn't do anything a promise returned by the function you pass it (the return value of that function is completely ignored), so the rejection goes unhandled. This is one of the promise anti-patterns: Don't provide a promise to something that won't handle it (like addEventListener on the web, or the Promise constructor, or forEach, ...).
Similarly, there's no reason to use new Promise in your async function at all. async functions already return promises. That's another anti-pattern, sometimes called the explicit promise construction anti-pattern. (But see below if you're wrapping an old-fashioned callback API.)
If you remove the unnecessary new Promise, it works as you expect (I also updated run to call callback rather than ignoring it and calling throwError directly):
async function main() {
try {
await run(throwError);
} catch (error) {
console.log("main catch error", error);
}
}
async function run(callback) {
return await callback();
}
async function throwError() {
throw new Error("custom error");
}
process.on("unhandledRejection", (reason, promise) => {
console.log("unhandledRejection - reason", reason, promise);
});
main();
About the return await callback(); in that example: Because whatever you return from an async function is used to settle the function's promise (fulfilling it if you return a non-thenable, resolving it to the thenable if you return one), and that return is at the top level of the function (not inside a try/catch or similar), you could just write return callback();. In fact, you could even remove async from run and do that. But keeping the await makes the async stack trace clearer on some JavaScript engines, more clearly indicates what you're doing, and keeps working correctly even if someone comes along later and puts a try/catch around that code.
In a comment, you said that you used new Promise because you were wrapping around a callback-based API. Here's how you'd do that (see also the answers here):
// Assuming `callback` is a Node.js-style callback API, not an
// `async` function as in the question:
function run(callback) {
return new Promise((resolve, reject) => {
callback((err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
but, you don't have to do that yourself if the callback-based API uses the standard Node.js convention as above, there's a promisify function in utils.
You don't want to throw an error in this case, you want to invoke reject:
return new Promise((resolve, reject) => {
reject('custom error')
});
If the error being thrown is out of your control, you can catch it inside the promise implementation and reject in that case.

Promises: how to return a promise which has a otherwise error handler

I am using tableau js api and calling worksheet.applyFilterAsync & workbook.changeParameterValueAsync methods based on certain conditions. These methods have an otherwise error handler.
I am also in between the executing flow return my custom promise like
return new Promise((resolve, reject) => {
resolve(true)
});
the issue is when i am returning promise and not changeParameterValueAsync or worksheet.applyFilterAsync i am getting an error
TypeError: undefined is not a function (near '...}).otherwise(function (err) {...')
looks like the promise i return does not have a otherwise error handler?
relevant code is as follows
const applyStep = (step) => {
if(step.step_type == 'filter') {
return worksheet.applyFilterAsync(step.name, values, 'replace');
} else if(step.step_type == 'parameter') {
return workbook.changeParameterValueAsync(`${step.name}`, value);
} else {
return new Promise((resolve, reject) => {
resolve(true)
});
}
};
const applyFilters = (counter) => {
return new Promise((resolve, reject) => {
let filter = filters[counter];
if(filter) {
applyStep(filter).then(promiseTimeout(filter.delay)).then(() => resolve(applyFilters(++counter))).otherwise(function(err) {
reject(err);
});
} else {
resolve(true);
}
});
};
I need the otherwise error handler to handle error returned by tableau api methods but it does not work in cases where i am returning my custom promise, may be because the promise i am returning does not have an otherwise error handler?
How can address this any help in fixing this will be really great, Thanks.
From the tableau docs:
otherwise(errback: Function) Promise Registers a rejection handler. Shortcut for then(null, errback).
So in your code, you could do
applyStep(filter)
.then(promiseTimeout(filter.delay))
.then(() => resolve(applyFilters(++counter)))
.then(null, function(err) {
reject(err);
});
That is instead of .otherwise(err) you can use .then(null, err).
The issue is that you are currently mixing two different types of promises. The tableau Promise and the standard JavaScript Promise. These two have different methods which currently results in issues. You can pass a tableau Promise to Promise.resolve() to convert it into a standard JavaScript Promise.
return Promise.resolve(worksheet.applyFilterAsync(step.name, values, 'replace'));
When working with the promise you now have to work with then(), catch() and finally() instead of then(), otherwise() and always().

How to propagate resolve() in nested promises?

I'm writing a React application and in particular cases I have to resolve nested promises. The code works fine but I can't propagate the resolve() function up to the outer level, thus I'm not able to get the returning value.
This is the code:
writeData(data) {
this.store.dispatch({type: "START_LOADER"})
return new Promise((resolve, reject) => {
this.manager.isDeviceConnected(this.deviceId).then(res => {
this.manager.startDeviceScan(null, null, (error, device) => {
if (device.id === this.deviceId) {
resolve("test") // -> this is propagate correctly
device.connect().then((device) => {
this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
return device.discoverAllServicesAndCharacteristics()
}).then((device) => {
device.writeCharacteristicWithoutResponseForService(
data.serviceId,
data.charId,
data.dataToWrite
).then(res => {
resolve("test2") // -> this is not propagated
}).catch(error => {
reject(error.message)
})
}).catch((error) => {
reject(error.message)
});
}
});
}).catch(error => {
reject(error.message)
})
})
}
...
...
async writeAsyncData(data) {
await this.writeData(data)
}
When I call this function:
this.writeAsyncData({data}).then(response => {
// here I expect whatever parameter I have passed to resolve()
console.log(response)
})
In case I leave resolve("test") uncommented I can console.log it without any problem, but if I comment it, the resolve("test2") doesn't show in console.log and response is undefined.
How can I make sure that even the nested parameter of the inner resolve reach the console.log ?
To nest promises properly, you do NOT wrap them in yet another manually created promise. That is an anti-pattern. Instead, you return the inner promises and that will then chain them. Whatever the inner-most promise returns will be the resolved value for the whole chain.
In addition, when you have any asynchronous operations that return callbacks, you must promisify them so that you are doing all your asynchronous control flow with promises and can consistently do proper error handling also. Do not mix plain callbacks with promises. The control flow and, in particular, proper error handling gets very, very difficult. One you start with promises, make all async operations use promises.
While this code is probably simplest with async/await, I'll first show you how you properly chain all your nested promises by returning every single inner promise.
And, to simplify your nested code, it can be flattened so that rather than each level of promise making deeper indentation, you can just return the promise back to the top level and continue processing there.
To summarize these recommendations:
1. Don't wrap existing promises in another manually created promise. That's a promise anti-pattern. Besides being unnecessary, it's very easy to make mistakes with proper error handling and error propagation.
2. Promisify any plain callbacks. This lets you do all your control flow with promises which makes it a lot easier to avoid errors or tricky situations where you don't know how to propagate errors properly.
3. Return all inner promises from within the .then() handlers to properly chain them together. This allows the inner-most return value to be the resolved value of the whole promise chain. It also allows errors to properly propagate all the way up the chain.
4. Flatten the chain. If you have multiple promises chained together, flatten them so you are always returning back to the top level and not creating deeper and deeper nesting. One case where you do have to make things deeper is if you have conditionals in your promise chain (which you do not have here).
Here's your code with those recommendations applied:
// Note: I added a timeout here so it will reject
// if this.deviceId is never found
// to avoid a situation where this promise would never resolve or reject
// This would be better if startDeviceScan() could communicate back when
// it is done with the scan
findDevice(timeout = 5000) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error("findDevice hit timeout before finding match device.id"));
}, timeout);
this.manager.startDeviceScan(null, null, (error, device) => {
if (error) {
reject(error);
clearTimeout(timer);
return
}
if (device.id === this.deviceId) {
resolve(device);
clearTimeout(timer);
}
});
});
}
writeData(data) {
this.store.dispatch({type: "START_LOADER"});
return this.manager.isDeviceConnected(this.deviceId).then(res => {
return this.findDevice();
}).then(device => {
return device.connect();
}).then(device => {
this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
return device.discoverAllServicesAndCharacteristics();
}).then(device => {
return device.writeCharacteristicWithoutResponseForService(
data.serviceId,
data.charId,
data.dataToWrite
);
}).then(res => {
return "test2"; // this will be propagated
});
}
Here's a version using async/await:
findDevice(timeout = 5000) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error("findDevice hit timeout before finding match device.id"));
}, timeout);
this.manager.startDeviceScan(null, null, (error, device) => {
if (error) {
reject(error);
clearTimeout(timer);
return
}
if (device.id === this.deviceId) {
resolve(device);
clearTimeout(timer);
}
});
});
}
async writeData(data) {
this.store.dispatch({type: "START_LOADER"});
let res = await this.manager.isDeviceConnected(this.deviceId);
let deviceA = await this.findDevice();
let device = await deviceA.connect();
this.store.dispatch({type: "SET_STATUS", payload: "Device is connected!\n"})
await device.discoverAllServicesAndCharacteristics();
let res = await device.writeCharacteristicWithoutResponseForService(
data.serviceId,
data.charId,
data.dataToWrite
);
return "something"; // final resolved value
}
Note: In your original code, you have two overriding definitions for device. I left that there in the first version of the code here, but changed the first one to deviceA in the second one.
Note: As your code was written, if this.manager.startDeviceScan() never finds a matching device where device.id === this.deviceId, your code will get stuck, never resolving or rejecting. This seems like a hard to find bug waiting to happen. In the absolute worst case, it should have a timeout that would reject if never found, but probably the implementation of startDeviceScan needs to communicate back when the scan is done so the outer code can reject if no matching device found.
Note: I see that you never use the resolved value from this.manager.isDeviceConnected(this.deviceId);. Why is that? Does it reject if the device is not connected. If not, this seems like a no-op (doesn't do anything useful).
Note: You call and wait for device.discoverAllServicesAndCharacteristics();, but you never use any result from it. Why is that?

Why i need to resolve a response in promise

I have a promise like below:
let promise = new Promise((resolve, reject) => {
axios.post("https://httpbin.org/post", params, header)
.then(response => {
resolve(Object.assign({}, response.data));
// resolve("aaaa");
console.log(response);
})
.catch(error => reject(error))
});
Why i need to resolve this promise with response data?
What happens if i replace resolve(Object.assign({}, response.data)); line by resolve("aaaa"); ?
Any one can help me? Thank you.
Something that's worth mentioning is axios.post() already returns a Promise so you needn't wrap it in another promise.
This will work instead:
let promise = axios.post("https://httpbin.org/post", params, header)
.then(response => {
console.log(response);
return Object.assign({}, response.data);
});
// Later on...
promise.then(data => {
console.log('Request successful:', data);
}, err => {
console.log('Request failed:', err);
});
Constructing a new Promise object is only necessary when you aren't chaining off an existing promise, like in this example:
function delay(duration) {
return new Promise(resolve => setTimeout(resolve, duration));
}
delay(1000).then(() => {
console.log('this code is delayed by 1s');
});
resolve() does exactly what the name says: It resolves the promise returning the value inside the function call.
So if you have a promise and it is resolving with resolve('aaaaa'), this means your promise will return a successful state with it's value being 'aaaaa'.
You could also reject the promise. This would mean the call you did failed at some point. Analog to resolve(), reject() accepts a parameter that should be returned by the promise.
All it will do is call the success callback resolve with the argument "aaaa" instead of its original value.
Let's say you pass the callback function console.log. If the promise resolves, i.e. is successful, then the callback will be called with the passed argument (console.log("aaaa"))
If it doesn't resolve - if it's unsuccessful - then the reject callback will be called as per your .catch() statement.
Promises have two arguments, resolve and reject, that are used to send a response back to the invoking code.
If your promise completed its code without errors, then you resolve() it, by sending back the response (it can be whatever you want) and if it fails instead, you reject() it, usually passing the error as parameter.
The parameter of the resolve function will be sent back to the invoking function, in the then callback, while the parameter of the reject function can be found in the catch callback.
For example
function myFunction(){
return new Promise((resolve, reject) => {
try{
/* Do some async calls here */
resolve(result); //Result is what you will find in "then"
}catch(e){
reject(e);
}
});
}
myFunction().then((response) => {
/* Your logic here*/
}).catch((err) => {
console.error(err);
}
You can think the resolve as a return in an asynchronous context, while the reject is similar to throwing an exception that will be caught by the invoking code.
So resolve(myVariable) will return myVariable to the code that called the promise function, while resolve('aaa') will always return "aaa" to the invoking code.

I can't get the value of "result" in Node.js

In my console.log(info), I want to get the value of "result". But when I use console.log(info), I get Promise { <pending> }:
var info=new Promise((resolve, reject) => {
request(options, (err, res, body) => {
if (body.match('success') || body.match('code="25"')) {
resolve("success");
} else {
reject(body);
}
});
}).then(result => {
return result;
}).catch(err => {
console.log("error: " + err)
});
console.log(info);
I would like to get info == result. How can I do it?
Thanks
What happens here
First some explanation of what happens here. When you do something like this:
var info = new Promise((resolve, reject) => {
// ...
}).then(result => {
// ...
}).catch(err => {
// ...
});
then what ends up as the value of the info variable is a promise. That's because you start with a promise returned by the new Promise() constructor, you invoke its .then() method which returns a second promise, and on that second promise you invoke the .catch() method which returns a third promise - and this third promise is what gets saved into the info variable.
All of those method calls return immediately before the original HTTP request is finished so when you reach the next line:
console.log(info);
it's impossible to access the value of the response from the request() call because it didn't happen yet (the request() callback hasn't been called yet at this point). The info variable has a promise which is an object that you can use to register the callbacks that you want run when the value is eventually available. When you pass it to the console.log() it prints that it's a promise.
How to get the value
Now, when you want to print the value when it's finally available then you can attach a promise resolution callback to the promise that you have e.g. with a code like this:
info.then((value) => {
// you can use the value here
console.log('Value:', value);
}).catch((err) => {
// the promise got rejected
console.log('Error:', err);
});
If you are using a relatively recent version of Node (7.0+) then you could use await if you are inside of a function declared with an async keyword, to get the resolution value of the promise like this:
(async function () {
let value = await info;
console.log(value);
})();
or shorter:
(async () => {
console.log(await info);
})();
(If you are already inside of an async function then you don't need the (async () => { ... })(); wrapper.)
How it works
What the await keyword does is it yields the promise from an implicit generator function that passes the control to the outer coroutine control code which attaches a resolution and rejection handlers to that yielded promise and starts the generator again by either returning the resolved value from the await operator or raising an exception if the promise was rejected. The generator can then continue using the return value and catching the exception, or it can let the exception bubble to the outer blocks where it can be caught or converted to a rejection of the implicit promise returned by the async function if not handled along the way.
How you can use it
It may seem complicated but from the point of view of your code it lets you do things like:
let value = await fun();
(where fun() is a function that returns a promise) and have the resolved value of the promise available in the value variable (the real value, not a promise).
Remember that the await keyword throws an exception when the promise in question gets rejected. To handle that case you can use a try/catch block:
try {
let value = await fun();
// promise got resolved, you have value here:
console.log('Value:', value);
} catch (error) {
// promise got rejected, you have error here:
console.log('Error:', error);
}
which is equivalent of this code without the new syntax:
fun().then((value) => {
// promise got resolved, you have value here:
console.log('Value:', value);
}).catch((error) => {
// promise got rejected, you have error here:
console.log('Error:', error);
});
with the main difference being the variable scoping when you await on multiple promises in a single try block as opposed to using multiple chained .then() handlers, each returning a new promise.
Your code looked like it used await but it didn't
I added the example of how to fix your code with await because you wrote your code as if this:
var info = new Promise(...);
// here the 'info' variable is set to a promise
was really this:
var info = await new Promise(...);
// here the 'info' variable is set to the value
// that the promise eventually resolves to
More info
For more info, see:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
Node support
For support of that syntax in Node versions, see:
http://node.green/#ES2017-features-async-functions
In places where you don't have native support for async and await you can use Babel:
https://babeljs.io/docs/plugins/transform-async-to-generator/
or with a slightly different syntax a generator based approach like in co or Bluebird coroutines:
https://www.npmjs.com/package/co
http://bluebirdjs.com/docs/api/promise.coroutine.html
In your code, info is a promise. Thus, you don't compare info to anything. To get access to the result in a promise, you use .then() on the promise as in:
info.then(result => {
// test result here
}).catch(err => {
// handle error here
});
In addition, your .catch() handler is "eating" the error after logging it. If you don't rethrow the error or return a rejected promise from a .catch() handler, then the error is considered "handled" and the promise state changes to fulfilled.
So, you can do this:
let info = new Promise((resolve, reject) => {
request(options, (err, res, body) => {
if (body.match('success') || body.match('code="25"')) {
resolve("success");
} else {
reject(body);
}
});
}).catch(err => {
console.log("error: " + err);
// rethrow error so promise stays rejected
throw err;
});
info.then(result => {
// test result here
}).catch(err => {
// handle error here
});

Categories

Resources