Calling reject multiple times in the chain not being caught - javascript

I was expermenting with promises and error handling, but I cannot explain the behaviour of a piece of code. Promise.reject is called twice, the first is handled perfectly but the latter is not being caughed at all.
const a = Promise.resolve('test');
a
.then(item => Promise.reject('promise reject 1'))
.then(
(a) => {
console.log(a); // value: test
},
(err) => {
console.log(err); // value: promise reject 1
}
).then((a) => {
console.log('continue1'); // being called
Promise.reject('promise reject 2') // called but not caught
},
(err) => {
console.log(err); // never being called
})
.catch((err) => {
console.log(err); // never being called
});
I expected the catch to catch the error introduced at line 14 (Promise.reject('promise reject 2')), but the catch handler is never called

Promise.reject() just makes a rejected promise object. Your chain doesn't know anything about it unless you return it. If you return it, the next catch will catch it:
const a = Promise.resolve('test');
a
.then(item => Promise.reject('promise reject 1'))
.then(
(a) => {
console.log(a); // value: test
},
(err) => {
console.log(err); // value: promise reject 1
}
).then((a) => {
console.log('continue1'); // being called
return Promise.reject('promise reject 2') // << ---- Return me!
},
(err) => {
console.log(err); // never being called
})
.catch((err) => {
console.log("from catch:",err); // caught!
})
You can also throw in then():
const a = Promise.resolve('test');
a
.then(item => Promise.reject('promise reject 1'))
.then(
a => {
console.log(a); // value: test
},
err => {
console.log(err); // value: promise reject 1
})
.then(
a => {
console.log('continue1'); // being called
throw(new Error('promise reject 2')) // <-- no return required
},
err => {
console.log(err); // never being called
})
.catch(err => {
console.log("from catch:",err.message); // caught!
})

Related

How do i write promises in javascript

im trying to write a promise but seems to be missing something. here is my code:
const myPromise = new Promise(() => {
setTimeout(() => {
console.log("getting here");
return setinputs({ ...inputs, images: imageAsUrl });
}, 100);
});
myPromise
.then(() => {
console.log("getting here too");
firebase.database().ref(`collection/${idNode}`).set(inputs);
})
.then(() => {
console.log("all is set");
})
.catch((err) => {
console.log(err);
});
if i run the program, the first part of the promise is executing but all .then() functions arent executing. how do i fix this?
In this scheme, the promise callback has one (resolve) or two (resolve,reject) arguments.
let p = new Promise((resolve, reject)=> {
//do something
//resolve the promise:
if (result === "ok") {
resolve(3);
}
else {
reject("Something is wrong");
}
});
p.then(res => {
console.log(res); // "3"
}).catch(err => {
console.error(err); //"Something is wrrong
});
Of course, nowadays you can use async + await in a lot of cases.
You need to resolve the promise, using resolve() and also return the promise from firebase so the next .then in the chain works properly.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("getting here");
// You have to call resolve for all `.then` methods to be triggered
resolve({ ...inputs, images: imageAsUrl });
}, 100);
});
myPromise
.then((inputs) => {
console.log("getting here too");
// You have to return a promise in a .then function for the next .then to work properly
return firebase.database().ref(`collection/${idNode}`).set(inputs);
})
.then(() => {
console.log("all is set");
})
.catch((err) => {
console.log(err);
});

Promises can't run code after resolve/reject

I have async function in async function. In the second I must wait when promises resolve or reject and after run other code below. But if promise reject my code stopping and no run other functions. How I can fix it?
await axios.all(promises).then(res => {
axios.patch("/url", { foo: bar }).then(async () => {
const promises2 = arr.map(item => {
return axios.post("/url-2", item)
});
await Promise.all(promises2)
.then(() => console.log("resolved")) //this not calling ever
.catch(() => console.log("failed")) //this not calling ever
console.log("This console log ever not working")
})
})
Promises aren't chained properly, axios.patch(...) promise isn't returned. await is syntactic sugar for then and catch, its purpose is to get rid of nested functions where possible. It should be:
const res = await axios.all(promises)
await axios.patch("/url", { foo: bar })
const promises2 = arr.map(item => {
return axios.post("/url-2", item)
});
try {
await Promise.all(promises2)
console.log("resolved"))
} catch (err) {
console.log("failed");
}
The order of your code is wrong. Of course if the first promise is rejected, then the rest will not be called.
Try rewriting your code this way:
let res = await axios.all(promises).catch(() => { console.log("failed"); return false; });
if (!res) {
// Do something when rejected
....
}
// Call the 2nd promise
let res2 = await axios.path("/url", {foo: bar}).catch(() => {console.log("failed 2"); return false; });
if (!res2) {
// Do something when the 2nd promise is rejected
...
}
// Call your last promise
let res3 = await Promise.all(promises2).catch(() => {console.log("failed 3"); return false; });
if (!res3) {
// Do something if it is rejected again
....
}
// Otherwise, do your thing
Try this code, it should pinpoint where the error or rejection is occurring (i.e. it's definitely before Promise.all(promises2) is run
await axios.all(promises)
.then(res => axios.patch("/url", { foo: bar }), err => {
throw `all(promises) failed with ${err}`;
})
.then(() => {
const promises2 = arr.map(item => {
return axios.post("/url-2", item);
});
return Promise.all(promises2)
.then(() => console.log("resolved")) //this not calling ever
.catch(err => {
throw `all(promises2) failed with ${err}`;
});
}, err => {
throw `patch failed with ${err}`;
})
.catch(err => console.error(err));
Note I've removed async/await, because in the code you've posted it is totally unnecessary

Nested Promise with setTimeout

I have a nested promise. The promise resolves or rejects based on another promise resolving or rejecting with a setTimeout of 0 so as not to clog:
return new Promise((resolve, reject) => {
promiseInst
.then(value => {
executeSoon(() => {
dispatch({ ...action, status: "done", value: value });
resolve(value);
});
})
.catch(error => {
executeSoon(() => {
dispatch({
...action,
status: "error",
error: error.message || error
});
reject(error);
});
});
});
the executeSoon() is executeSoon(fn) { setTimeout(fn, 0); }
How do I simplify this? Tried to get rid of the outer promise wrapper and returning the inner promise directly but got stuck with the resolve and reject not being defined. So I thought I'd just return a Promise.resolve(value) from inside the .then and Promise.reject(error) from the catch, but this does not work either. I feel it might be possible to simplify this, but can't quite get my head around it.
Just use a promising timer:
const timer = ms => new Promise(res => setTimeout(res, ms));
Then its as simple as:
timer(0).then(() => promiseInst)
.then(...)
.catch(...)
.... but actually you dont need the timer as promises are guaranteed to resolve one tick after resolve() was called.
Remember that a then handler is always executed asychronously, there's no need for setTimeout(..., 0) in the normal case. So if the setTimeout doesn't have another purpose:
return promiseInst
.then(value => {
dispatch({ ...action, status: "done", value: value });
return value;
})
.catch(error => {
dispatch({ ...action, status: "error", error: error.message || error });
throw error;
});
If it does something important, I'd probably have a promise-enabled version, something along these lines:
const setTimeoutPromise = (delay = 0) => new Promise(resolve => {
setTimeout(resolve, delay);
});
Then see the lines with *** below:
return promiseInst
.then(value => setTimeoutPromise().then(() => value) // ***
.then(value => {
dispatch({ ...action, status: "done", value: value });
return value;
})
.catch(error => setTimeoutPromise().then(() => { // ***
dispatch({ ...action, status: "error", error: error.message || error });
throw error;
})); // <== *** Note one more )

Object mapping: Promise.all to catch iteration end and error

Let's say I want to perform a function on every value of an object.
How can I catch the end of the iteration and to know that no error has been made running the function on each value?
Object.keys(result).map(function(key, index) {
var value = result[key];
functiontorun(value, (result, error) => {
if (error) {
// catch the error
}
});
// AT THE END OF THE ITERATION
// if no error at all => then something
// if at least one error => then something else
});
Turn each request into a promise, and use Promise.all on the result:
const allPromises = Object.keys(result).map(function(key, index) {
var value = result[key];
return new Promise((resolve, reject) => {
functiontorun(value, (result, error) => {
if (error) {
// catch the error
reject(error);
} else resolve();
});
});
});
Promise.all(allPromises)
.then(() => console.log('all successful'))
.catch(err => console.log('at least one error ' + err));

Why doesn't the method `shouldNotResolve` catch the rejected promise?

I would expect that a.shouldNotResolve() would 'catch' the rejected promise in a.cancelOrder, and return 'expected this to catch', but instead it resolves, returning 'promise resolved anyway'.
const a = {
cancelOrder: function(){
return Promise.reject('something broke')
.then((x) => {
return x;
})
.catch((e) => {
console.log('this caught the error', e);
});
},
shouldNotResolve: function() {
return this.cancelOrder()
.then(() => {
console.log('promise resolved anyway');
})
.catch(() => {
console.log('expected this to catch');
});
}
}
a.shouldNotResolve(); // "promise resolved anyway"
Why does a.cancelOrder reject, but a.shouldNotResolve resolves anyway?
Thank you.
Because you catch this error inside
cancelOrder: function(){
return Promise.reject('something broke')
.then((x) => {
return x;
})
.catch((e) => { // <-- this catches all errors
console.log('this caught the error', e);
});
},
catch is literally like try-catch for promises. If you already caught an exception outer one wont catch.
try {
try {
throw new Error('Failed')
} catch(e) { // first catch
console.log('inner', e.message)
}
} catch(e) { // second catch
console.log('outer', e.message)
}
As #robertklep suggests you might want to re-throw
cancelOrder: function(){
return Promise.reject('something broke')
.then((x) => {
return x;
})
.catch((e) => { // <-- this catches all errors
console.log('this caught the error', e);
return Promise.reject(e) // re-reject
});
},
const rejected1 = Promise.reject(1)
const resolved = rejected1
.catch(x => console.log(x))
.then(() => console.log('resolved'))
const rejected2 = rejected1.catch(() => {
console.log('caught but rethrow')
return Promise.reject(2)
})
rejected2.catch(x => console.log(x))
Because it's already caught ("catched"?) earlier in the chain, as seen in the cancelOrder function. If you want to catch it there but still pass the rejection down the chain you need to throw it again:
cancelOrder: function(){
return Promise.reject('something broke')
.then((x) => {
return x;
})
.catch((e) => {
console.log('this caught the error', e);
throw e;
});
}
Write the catch in cancelOrder outside the promise flow, so it doesn't put the promise back in success state.
const a = {
cancelOrder: function(){
const result = Promise.reject('something broke')
.then((x) => {
return x;
});
result.catch((e) => {
console.log('this caught the error', e);
});
return result;
},
shouldNotResolve: function() {
return this.cancelOrder()
.then(() => {
console.log('promise resolved anyway');
})
.catch(() => {
console.log('expected this to catch');
});
}
}
As mentioned in another answer, another option, if you want to put the catch in the promise flow, is to re-throw the error.
You might also consider writing an interceptor which does this for you:
function doCatch(fn) {
return function(reason) {
fn(reason);
throw reason;
});
}
Now you can write
cancelOrder: function(){
return Promise.reject('something broke')
.then((x) => {
return x;
})
.catch(doCatch(err => console.err("this caught the error", e)));
}
By the way, .then((x) => { return x; }) is a no-op.

Categories

Resources