I'm learning chaining in JS Promises from this site. Based on the high level example, I've written following code to understand error propagation better.
var promise = new Promise((resolve, reject) => {
reject('Rejected!');
});
promise.then(()=>new Promise ((resolve, reject) => resolve('Done!')), () => console.log('Failure of first Promise'))
.then(() => console.log('Success of nested Promise'), () => console.log('Failure of nested Promise'));
console.log('This will be still printed first!');
Here when I'm rejecting the first promise, It is giving logging Failure of first Promise and then Success of nested Promise.
Now I wonder how It's going in the success callback of nested Promise? As explained in the above mentioned article, it's clear that even one promise is failed (rejected), the failure callback should be invoked.
What I'm missing here? Thanks.
The code you have written is like
var promise = new Promise((resolve, reject) => { reject('Rejected!'); });
promise
.then(()=>new Promise ((resolve, reject) => resolve('Done!')))
.catch(() => console.log('Failure of first Promise'))
.then(() => console.log('Success of nested Promise'))
.catch(() => console.log('Failure of nested Promise'));
console.log('This will be still printed first!');
since catch also returns a promise, the then chained with catch will also be triggered, if there is only one catch at end of all then, that catch will be triggered without any then.
you can do the following to overcome this issue,
promise
.then(()=>new Promise ((resolve, reject) => resolve('Done!')))
.then(() => console.log('Success of nested Promise'))
.catch(() => console.log('Failure of Promise'));
console.log('This will be still printed first!');
Second then callback catches the error from previous promise (catches as in try..catch). In this specific case (there's no chance that first then callbacks result in rejection) this is the same as:
promise // rejected promise
.then(()=>new Promise ((resolve, reject) => resolve('Done!'))) // skips rejected promise
.catch(() => console.log('Failure of first Promise')) // results in resolved promise
.then(() => console.log('Success of nested Promise')) // chains resolved promise
.catch(() => console.log('Failure of nested Promise')); // skips resolved promise
This is because
The catch() method returns a Promises
MDN source - Promise.prototype.catch()
"use strict";
new Promise((resolve, reject) => {
reject('Error');
}).catch(err => {
console.log(`Failed because of ${err}`);
}).then(() => {
console.log('This will be called since the promise returned by catch() is resolved');
});
This will log
Failed because of Error
This will be called since the promise returned by catch() is resolved
Related
I saw following implementation of Promise.race().
I am finding it difficult of understand, how it's working.
const race = function(promisesArray) {
return new Promise((resolve, reject) => {
promisesArray.forEach((innerPromise) => {
Promise.resolve(innerPromise)
.then(resolve, reject)
.catch(reject);
});
});
};
1st part: Is the below statement true?
Promise.resolve(innerPromise) will always return a resolved promise with innerPromise as value and as it always resolves, I will always end up in .then block.
2nd Part:
I read in the explanation that resolve and reject passed to .then block will be called on resolution of innerPromise. Why?, shouldn't it be as Promise.resolve(innerPromise) always resolve, always 1st callback of .then block should get called?
I think I am missing something very basic. I have tried to find the solution but not able to find an explanation that clears my doubts.
The purpose of Promise.resolve in that code is to allow elements of the array to not be promises. They could be arbitrary thenables, or they could just be regular values (which will indeed become fulfilled promises).
const existingValue = 5;
const existingPromise = Promise.reject(new Error("blah"));
const existingThenable = {
then() {
console.log("never going to resolve >:)");
}
};
Promise.race([existingValue, existingPromise, existingThenable]).then(
value => { console.log("got a value", value); },
error => { console.log("got an error", error); },
);
1st part: Is the below statement true?
Promise.resolve(innerPromise) will always return a resolved promise with innerPromise as value and as it always resolves, I will always end up in .then block.
If you try removing existingValue from the array passed to Promise.race above, you’ll see that Promise.resolve doesn’t necessarily return a resolved promise; specifically, when it’s passed a promise or other thenable, it returns a promise that settles the same way (although when passed the same type of promise, it actually satisfies that obligation by returning the same promise object). So no, it’s not true. I think that answers part 2, too.
Additionally, although you didn’t bring it up: I’m pretty sure the additional .catch(reject) is unreachable/useless, at least for the standard ES Promise implementation.
Promise.resolve(anotherPromise) will always assume the state of anotherPromise, so if it anotherPromise is rejected, then the one from Promise.resolve() will also be rejected. Or if anotherPromise is fulfilled, the Promise.resolve() one will also be fulfilled with its value.
const rejectedPromise = Promise.reject("boom");
Promise.resolve(rejectedPromise)
.then(result => console.log("success:", result))
.catch(error => console.log("failure:", error));
See the documentation on MDN for Promise.resolve().
As for why the code is using Promise.resolve() instead of directly
innerPromise
.then(resolve, reject)
.catch(reject);
Promise.resolve() is useful when the input is not necessarily a promise. It can convert a plain value to a promise or an arbitrary thenable (potentially another promise implementation) to a vanilla JavaScript promise thus allowing for uniform way of handling the result.
Perhaps this is defensive coding or just allows for calling race(asyncResult, 42). The intention is not clear.
The resolve or reject parameters of into the executor function are noop when repeatedly called. A promise can reach a single final state - calling resolve/reject after that has no effect. Thus from the whole array, the first promise which leaves the pending state will determine what the promise constructor will be resolved as.
const p1 = new Promise((resolve, reject) => {
Promise.resolve("p1 success")
.then(resolve, reject)
.catch(reject);
Promise.reject("p1 failure")
.then(resolve, reject)
.catch(reject);
});
const p2 = new Promise((resolve, reject) => {
Promise.reject("p2 failure")
.then(resolve, reject)
.catch(reject);
Promise.resolve("p2 success")
.then(resolve, reject)
.catch(reject);
});
const p3 = new Promise((resolve, reject) => {
Promise.resolve("p3 hello")
.then(resolve, reject)
.catch(reject);
Promise.resolve("p3 world")
.then(resolve, reject)
.catch(reject);
});
const p4 = new Promise((resolve, reject) => {
Promise.reject("p4 foo")
.then(resolve, reject)
.catch(reject);
Promise.reject("p4 bar")
.then(resolve, reject)
.catch(reject);
});
p1.then(console.log, console.error);
p2.then(console.log, console.error);
p3.then(console.log, console.error);
p4.then(console.log, console.error);
Therefore, by looping and attaching the same resolve and reject to all promises, race will only resolve with the same outcome of the first promise to resolve. This matches the JavaScript implementation of Promise.race():
Return value
A Promise that asynchronously settles with the eventual state of the first promise in the iterable to settle. In other words, it fulfills if the first promise to settle is fulfilled, and rejects if the first promise to settle is rejected. The returned promise remains pending forever if the iterable passed is empty. If the iterable passed is non-empty but contains no pending promises, the returned promise is still asynchronously (instead of synchronously) settled.
N.B. iterable is the input to Promise.race(). It matches promisesArray of race().
With all that said, the following construct seems entirely superfluous:
p
.then(resolve, reject)
.catch(reject);
The second parameter to .then() is the onRejected callback. So if p is rejected, the second argument to .then() would be used to handle that. The extra .catch() would handle errors coming from either resolve or reject in the .then()
Promise.resolve("foo")
.then(
result => { throw `Fulfilled with ${result}. Throwing after success.` },
error => {throw `Fulfilled with ${error}. Throwing after error` }
)
.catch(errorFromThen => console.log(`Error in .catch() is: ${errorFromThen}`));
Promise.reject("bar")
.then(
result => { throw `Fulfilled with ${result}. Throwing after success.` },
error => {throw `Fulfilled with ${error}. Throwing after error` }
)
.catch(errorFromThen => console.log(`Error in .catch() is: ${errorFromThen}`));
Yet, neither resolve nor reject from the executor function can throw/reject in a plain Promise constructor.
//more verbose onError and .catch() handlers in order to showcase what gts shown or not
const p = new Promise((resolve, reject) => {
Promise.reject("hello")
.then(
resolve,
error => {
console.log("inside the onReject in .then()", error);
return reject(error);
})
.catch(error => {
console.log("inside the .catch()", error);
return reject(error);
});
Promise.reject("world")
.then(
resolve,
error => {
console.log("inside the onReject in .then()", error);
return reject(error);
}
)
.catch(error => {
console.log("inside the .catch()", error);
return reject(error);
});
});
p.then(console.log, console.error);
The extra .catch() is thus not used.
Overall, race() behaves like the vanilla JavaScript Promise.race(). The only major difference is that Promise.race() accepts any iterable, while race() only handles arrays.
const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));
const array = [delay(300, "a"), delay(100, "b"), delay(200, "c")];
const iterable = array.values();
Promise.race(iterable)
.then(result => console.log(`First to fulfil was ${result}`));
const race = function(promisesArray) {
return new Promise((resolve, reject) => {
promisesArray.forEach((innerPromise) => {
Promise.resolve(innerPromise)
.then(resolve, reject)
.catch(reject);
});
});
};
const delay = (ms, value) =>
new Promise(resolve => setTimeout(resolve, ms, value));
const array = [delay(300, "a"), delay(100, "b"), delay(200, "c")];
const iterable = array.values();
race(iterable)
.then(result => console.log(`First to fulfil was ${result}`))
.catch(console.error);
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.
function getPromise() {
return new Promise((resolve, reject) => {
setTimeout(reject, 2000, new Error('fail'));
});
}
const promise1 = getPromise();
promise1.catch(() => {
// NOP
});
promise1
.then(() => console.log('then promise1'))
.catch(() => console.error('catch promise1'));
const promise2 = getPromise().catch(() => {
// NOP
});
promise2
.then(() => console.log('then promise2'))
.catch(() => console.error('catch promise2'));
Output
catch promise1
then promise2
Explanations
Here promise2 will be processed differently then promise1. While promise1 will be rejected with 'fail' error, promise2 will be resolved with undefined.
My environment
Ubuntu 14.04, Node.js 10.1.0
Question
I think this behavior is not obvious. Why does the code work this way?
Because you chain the second then not to the rejecting promise, but to the promise returned by catch, which will resolve to whatever you return from the catch handler.
Promise(promise1)
-❎-> catch
-✔-> then -❎- > catch
-❎-----------------^
Promise
-❎-> catch(promise2)
-✔-> then -❎-> catch
-❎-----------------^
-✔------------^
In the first case, the promise rejects, so it will enter the catch directly assigned to it, and it will skip the then, going directly to the chained catch. In the second case, the promise rejects and the catch will be executed, but then it seems to handle the error, so the chained then will be called. If you don't want that, the catch has to rethrow the error, then the then will be skipped and it also enters the chained catch.
Catch returns a new promise:
let orig = Promise.reject("fail")
let p = orig.catch(console.log)
console.log("is p the same as orig? ", p === orig)
p.then(() => console.log('p is a promise?', p instanceof Promise))
So when you call
const promise2 = getPromise().catch(() => { //..})
and assign the value to a promise2 that is a brand new promise returned from catch. In the first case you use the oringal promise1 in both statements.
To make the first statement work like the second, you would need to do something like:
function getPromise() {
return new Promise((resolve, reject) => {
setTimeout(reject, 2000, new Error('fail'));
});
}
let promise1 = getPromise();
// reassign the value of promise1
promise1 = promise1.catch(() => {
// NOP
});
promise1
.then(() => console.log('then promise1'))
.catch(() => console.error('catch promise1'));
Which is also the equivalent of just doing:
promise1
.catch(() => {/* noop */ })
.then(() => console.log('then promise1'))
.catch(() => console.error('catch promise1'));
Also, if you want to make sure the error is passed down the chain, you can always return a rejected promise from catch:
let promise1 = getPromise().catch((e) => {
return Promise.reject(e)
});
I have two promises, one rejected and other resolved. Promise.all is called. It executed the catch block of Promise.all as one of the promises is rejected.
const promise1 = Promise.resolve('Promise 1 Resolved');
const promise2 = Promise.reject('Promise 2 Rejected');
const promise3 = Promise.all([promise1, promise2])
.then(data => {
console.log('Promise.all Resolved', data);
})
.catch(error => {
console.log('Promise.all REJECTED', error);
})
setTimeout(() => {
console.log(promise1, promise2, promise3)
}, 200);
If I don't have the catch on Promise.all(), the value remains as Rejected, ie
const promise3 = Promise.all([promise1, promise2])
.then(data => {
console.log('Promise.all Resolved', data);
})
Am I missing something about promises.
I see that its answer but I think I can clarify a bit more.
Please remember that each then() or catch() return a Promise. (If you don't have any explicit return in callback, both will return Promise.resolve(undefined)). Therefore after the promise has resolved, the value of entire promise chain will be the promise returned by last then();
Example:
promise = Promise.resolve(1)
.then(() => Promise.resolve(2))
.then(() => Promise.resolve(3));
console.log(promise);
setTimeout(() => {
console.log(promise)//Promise {<resolved>: 3}
}, 0)
catch() works in exactly like then(). The only difference is that its called on rejected promises rather then resolved.
In following example, I just replace all resolve by reject to demonstrate that.
promise = Promise.reject(1)
.catch(() => Promise.reject(2))
.catch(() => Promise.reject(3));
console.log(promise);
setTimeout(() => {
console.log(promise)//Promise {<rejectd>: 3}
}, 0)
Now coming to your question. Value of Promise.all() is a rejected promise, since one of the promise in array is rejected. If you have a catch block in chain, control will go to that catch block which will return a Promise.resolve(undefined). If you have no catch block in the chain, you will get what you have: a rejected promise.
A catch on a Promise acts the same as a try {} catch {} block, in that you have captured the error state and the program will continue to function as normal.
That's why, when you omit the catch, your promise state is "rejected".
If, after having caught the error, you want to return the promise state as rejected you need to return a rejected promise from the catch handler:
const promise3 = Promise.all([promise1, promise2])
.catch(error => {
console.log("REJECTED", error);
return Promise.reject(error);
});
console.log(promise3); // [[PromiseStatus]]: "rejected"
Similar to doing a throw inside a try {} catch { throw; } block
So here is a true beauty from #Roamer-1888:
executePromiseLoop(myArray).catch(logError).then(() => console.log('yay!'));
function executePromiseLoop(arr) {
return arr.reduce(function(promise, email) {
return promise.then(function() {
return myCustomPromise(email);
});
}, Promise.resolve());
}
It's a promise loop that executes serially. There are two concerns I have:
What happens if a promise in the loop fails, does it quit the loop?
Should I implement a catch inside the loop, or will a fail propagate back up to the calling function?
Should I insert a catch in the loop?
function executePromiseLoop(arr) {
return arr.reduce(function(promise, email) {
return promise.catch(logError).then(function() {
return myCustomPromise(email);
});
}, Promise.resolve());
}
What happens if a promise in the loop fails, does it quit the loop?
Yes. If a single promise is rejected, all the next promises will not be executed. See this code for example:
Promise.resolve()
.then(Promise.reject)
.then(() => console.log('this will not be executed'))
.catch(() => console.log('error'))
The promise in the third line will not be executed, because the promise before it was rejected.
Should I implement a catch inside the loop, or will a fail propagate back up to the calling function?
The reject message will propagate, so you don't need to use a catch inside the loop. See this code for example:
Promise.resolve()
.then(() => Promise.resolve())
.then(() => Promise.reject('something went wrong'))
.then(() => Promise.resolve())
.then(() => Promise.resolve())
.catch(error => console.log(error))