I have a popular scenario where I need to create one promise that returns data which is fed to a second promise.
If the first promise fails, I need to cancel the second promise.
In 'Promise' land it would look something like this:
Fn1.doPromise( initialData )
.then(info => {
Fn2.doPromise( info )
.then(result => {
//success - return result
})
.catch(error => {
//error
});
})
.catch(error => {
//cancel 2nd promise and show error
});
Now I am trying to learn the best way to do this using Observables with something like RxJS. Can anyone give me a good solution ?
Thank in advance !
The general issue of error handling with RxJS is dealt with here. Main points are :
Catching Errors (with catch operator, either at instance level or at class level)
Ignoring Errors with onErrorResumeNext
Retrying Sequences (with retry)
Ensuring Cleanup (with finally)
Ensuring Resource Disposal (with finally or using)
Delaying Errors (with mergeDelayError)
About your specific question you can use Rx.Observable.fromPromise to convert a promise into an observable; Rx.Observable.prototype.catch to catch errors as they occur.
Rx.Observable.fromPromise(Fn1.doPromise( initialData ))
.flatMap(info => {
return Rx.Observable.fromPromise(Fn2.doPromise( info ))
.flatMap(result => {
//success - return result
// !! You must return an observable or a promise here !!
})
.catch(error => {
//error
// !! You must return an observable here !!
});
})
.catch(error => {
//cancel 2nd promise and show error
// !! You must return an observable here !!
});
Examples :
first error handler activated
no error
inner error handler activated
I was also able to find a nice generic solution for my promise 'chaining' after doing some more research. With this I can use an many promises as I need.
const flatMapAll = (...fns) =>
fns.reduce((acc, fn) =>
acc.flatMap(fn), Rx.Observable.just())
flatMapAll( p1 ,p2 ,p3).subscribe();
Related
I've just seen this code:
const buildSW = () => {
// This will return a Promise <- that comment was present on the code
workboxBuild
.injectManifest({
swSrc: 'src/sw.js'
// some code
})
.then(({ count, size, warnings }) => {
// some code
})
.catch(console.error);
};
buildSW();
The code seems to be working, has been in production like 1 year, but I get confused with the comment of This will return a Promise because I don't see that there is actually a return statement, and the whole code is inside { }.
Shouldn't be?:
return workboxBuild
.injectManifest({ ...etc
The code is part from this guides:
https://developers.google.com/web/tools/workbox/guides/generate-service-worker/workbox-build
where the return promise is present there. So how or why is working without return in the code showed above?
The comment is commenting on the line following it, and is redundant. E.g.
// a() will return a Promise
a().then(() => () // duh
...because it's equivalent to:
const promise = a();
promise.then(() => ()
Shouldn't be?: return workboxBuild.injectManifest({ ...etc
It depends on the contract you want buildSW to have.
As written, buildSW returns nothing and handles its own errors, using .catch(console.error) to emit them to console. Appropriate for event handlers — E.g. button.onclick = buildSW.
But if you expect to call buildSW from other places, a better contract is to return a promise, and leave error handling to the caller:
const buildSW = () => {
return workboxBuild.injectManifest({swSrc: 'src/sw.js'})
.then(({ count, size, warnings }) => {
// some code that may also fail
});
};
buildSW().catch(console.error);
I get confused with the comment because I don't see that there is actually a return statement, like it is present in the guide. Shouldn't it have one?
Yes, there should be a return statement. Sure, the function already catches errors and logs them, but it's still a good idea to always return a promise from asynchronous functions.
So how or why is working without return in the code showed above?
It's working only because the buildSW(); call you're doing doesn't try to use the promise that should be returned.
I have some hard time understanding what is the difference between using Promise.resolve() and simple using the promise.
The explanation seems a bit hard to get so i have a little example:
For example, i have a method that return a promise like this:
requestPokemon() {
return new Promise((resolve, reject) => {
axios.get("https://pokeapi.co/api/v2/pokemon/1").then(value => {
resolve(value);
})
.catch(error => {
reject(error);
})
});
}
so, now i can call this method and chain the promise, i figure two ways of do it, and can't get it when Promise.resolve is better or not, my issue is understanding that.
so i solved it in two ways:
first:
Promise.resolve(this.requestPokemon()).then(value => {
this.pokemon = value.data.name;
}).catch(error => {
console.log(error);
})
second:
this.requestPokemon().then(value => {
this.pokemon = value.data.name;
}).catch(error => {
console.log(error);
})
please i need a little explanation of the downsides and upsides on doing it one way above the other, i appreciate the help a lot.
Thanks
At first you dont need to construct a promise (thats an antipattern) in your requestPromise, just return the one from axios:
requestPokemon() {
return axios.get("https://pokeapi.co/api/v2/pokemon/1");
}
Now lets have a look at this line:
Promise.resolve(this.requestPokemon()).then(/*...*/)
It will create a new promise, that resolves or rejects when the axios promise resolves or rejects and adds a then handler to it. So it will be chained like this like this:
resolve() -> Promise (axios) -> Promise (Promise.resolve) -> then
reject() -> Promise (axios) -> Promise (Promise.resolve) -> catch
So as you can see Promise.resolve just passes through the data, so actually it is superflous. We actually just need:
resolve() -> Promise (axios) -> then
reject() -> Promise (axios) -> catch
Which can be done with:
this.requestPokemon().then(/*...*/, /*...*/);
please i need a little explanation of the downsides and upsides on doing it one way above the other
Adding Promise.resolve is just a bit more typing and makes it less readable, there is no syntactical difference.
Say we have 3 asynchronous tasks that return Promises: A, B and C. We want to chain them together (that is, for sake of clarity, taking the value returned by A and calling B with it), but also want to handle the errors correctly for each, and break out at the first failure. Currently, I see 2 ways of doing this:
A
.then(passA)
.then(B)
.then(passB)
.then(C)
.then(passC)
.catch(failAll)
Here, the passX functions handle each of the success of the call to X. But in the failAll function, we'd have to handle all of the errors of A, B and C, which may be complex and not easy to read, especially if we had more than 3 async tasks. So the other way takes this into consideration:
A
.then(passA, failA)
.then(B)
.then(passB, failB)
.then(C)
.then(passC, failC)
.catch(failAll)
Here, we separated out the logic of the original failAll into failA, failB and failC, which seems simple and readable, since all errors are handled right next to its source. However, this does not do what I want.
Let's see if A fails (rejected), failA must not proceed to call B, therefore must throw an exception or call reject. But both of these gets caught by failB and failC, meaning that failB and failC needs to know if we had already failed or not, presumably by keeping state (i.e. a variable).
Moreover, it seems that the more async tasks we have, either our failAll function grows in size (way 1), or more failX functions gets called (way 2). This brings me to my question:
Is there a better way to do this?
Consideration: Since exceptions in then is handled by the rejection method, should there be a Promise.throw method to actually break off the chain?
A possible duplicate, with an answer that adds more scopes inside the handlers. Aren't promises supposed to honor linear chaining of functions, and not passing functions that pass functions that pass functions?
You have a couple options. First, let's see if I can distill down your requirements.
You want to handle the error near where it occurs so you don't have one error handler that has to sort through all the possible different errors to see what to do.
When one promise fails, you want to have the ability to abort the rest of the chain.
One possibility is like this:
A().then(passA).catch(failA).then(val => {
return B(val).then(passB).catch(failB);
}).then(val => {
return C(val).then(passC).catch(failC);
}).then(finalVal => {
// chain done successfully here
}).catch(err => {
// some error aborted the chain, may or may not need handling here
// as error may have already been handled by earlier catch
});
Then, in each failA, failB, failC, you get the specific error for that step. If you want to abort the chain, you rethrow before the function returns. If you want the chain to continue, you just return a normal value.
The above code could also be written like this (with slightly different behavior if passB or passC throws or returns a rejected promise.
A().then(passA, failA).then(val => {
return B(val).then(passB, failB);
}).then(val => {
return C(val).then(passC, failC);
}).then(finalVal => {
// chain done successfully here
}).catch(err => {
// some error aborted the chain, may or may not need handling here
// as error may have already been handled by earlier catch
});
Since these are completely repetitive, you could make the whole thing be table-driven for any length of sequence too.
function runSequence(data) {
return data.reduce((p, item) => {
return p.then(item[0]).then(item[1]).catch(item[2]);
}, Promise.resolve());
}
let fns = [
[A, passA, failA],
[B, passB, failB],
[C, passC, failC]
];
runSequence(fns).then(finalVal => {
// whole sequence finished
}).catch(err => {
// sequence aborted with an error
});
Another useful point when chaining lots of promises is if you make a unique Error class for each reject error, then you can more easily switch on the type of error using instanceof in the final .catch() handler if you need to know there which step caused the aborted chain. Libraries like Bluebird, provide specific .catch() semantics for making a .catch() that catches only a particular type of error (like the way try/catch does it). You can see how Bluebird does that here: http://bluebirdjs.com/docs/api/catch.html. If you're going to handle each error right at it's own promise rejection (as in the above examples), then this is not required unless you still need to know at the final .catch() step which step caused the error.
There are two ways that I recommend (depending on what you are trying to accomplish with this):
Yes, you want to handle all errors in the promise chain with a single catch.
If you need to know which one failed, you can reject the promise with a unique message or value like this:
A
.then(a => {
if(!pass) return Promise.reject('A failed');
...
})
.then(b => {
if(!pass) return Promise.reject('B failed');
...
})
.catch(err => {
// handle the error
});
Alternatively, you can return other promises inside of .then
A
.then(a => {
return B; // B is a different promise
})
.then(b => {
return C; // C is another promise
})
.then(c => {
// all promises were resolved
console.log("Success!")
})
.catch(err => {
// handle the error
handleError(err)
});
In each of those promises, you will want some kind of unique error message so you know which one failed.
And since these are arrow functions we can remove the braces! Just another reason I love promises
A
.then(a => B)
.then(b => C)
.then(c => console.log("Success!"))
.catch(err => handleError(err));
You can branch the promise-chain, but honestly, early error handling is not the proper way to do it, especially for woolly reasons like readability. True for synchronous code as well, i.e. don't try/catch every single function, or readability just goes in the trash.
Always pass up errors and "handle" them at the point where positive code flow resumes.
If you need to know how far things got, a trick I use is a simple progression counter:
let progress = "";
A()
.then(a => (progress = "A passed", passA(a)))
.then(B)
.then(b => (progress = "B passed", passB(b)))
.then(C)
.then(c => (progress = "C passed", passC(c)))
.catch(err => (console.log(progress), failAll(err)))
Fairly new to promises here, and i was wondering..
After taking a look at bluebird package for handling promises:
Creating a Resolver
Resolve on success / reject on failure
Returning the created resolver promise property
I've been wondering how can i achieve the same effect of rejecting a promise to raise a catch using packages that already create the promise, and I'm just using their .then chain?
What i mean is, using reject on my create resolver will eventually raise a catch to the user of this function.
How can i raise a catch if i dont have the resolver. chaining the promise as follows :
function doSomthing(): Promise<someValue>
return somePackage.someMethodWithPromise().then((result)=> {
return someValueToTheNextThen;
})
}
The only way I've seen some packages achieving that is by returning a { errors, result } object so that the next then can check if there are any errors and react to it, but I want to raise a catch and not check for errors in every single then i have..
Hope i made myself clear, please let me know if anything is missing.
Thanks in advance for the help!
Just as in synchronous code, you can achieve this by throwing an error. This is the idiomatic thing to do when you're using promises and are already inside a then chain:
function doSomething(): Promise<someValue>
return somePackage.someMethodWithPromise().then((result)=> {
if (badResult(result)) {
throw new Error('Bad result!');
}
return someValueToTheNextThen;
})
}
doSomething()
.then(result => {
// this will get skipped if an error was thrown
})
.catch(error => {
// error will be caught here if one was thrown
console.error(error);
});
The .then just returns a Promise as well. You can simply created a rejected promise and return it from there if you determine there’s an error.
return somePackage.someMethodWithPromise().then(result => {
if (result.erroneous()) return Promise.reject('erroneous data');
return someValueToTheNextThen;
})
You can also simply throw anError; from the then which would also be catched in a catch block.
I've the following promise which work well
troces.run(value, "../logs/env.txt")
.then(function (data) {
console.log(data);
return updadeUser(val, arg, args[1])
// Now here I need to add new method updateAddress(host,port,addr)
}).catch(function (err) {
console.error(err);
});
Now I need to add additional method call inside the the first .then
that the update user and the updateAddress will work together
My question are
assume that updateUser will need to start 10 ms after the update
address How is it recommended to do so?
in aspects of error handling if one of the process failed (send error message ) I Need to exit (process.exit(1);)
Use .all:
troces.run(value, "../logs/env.txt")
.then(data => {
console.log(data);
return Promise.all([updadeUser(val, arg, args[1]),
updateAddress(host,port,addr)]);
}); // no need to add catches bluebird will log errors automatically
If you really need the 10ms delay, you can do:
troces.run(value, "../logs/env.txt")
.then(data => {
console.log(data);
return Promise.all([updadeUser(val, arg, args[1]),
Promise.delay(10).then(x => updateAddress(host,port,addr))]);
}); // no need to add catches bluebird will log errors automatically
Although I suspect that you really just want updateUser to happen before updateAddress which can be easily solved with:
troces.run(value, "../logs/env.txt")
.then(data => {
console.log(data);
return updadeUser(val, arg, args[1]).then(_ => updateAddress(host,port,addr));
}); // no need to add catches bluebird will log errors automatically
If you need to exit on promise error, you can do:
process.on("unhandledRejection", () => process.exit(1));
Although I warmly recommend you create meaningful error messages, just a non-zero process exit code is hard to debug.