my question is similar to this one How to throw error from RxJS map operator (angular), but I'm on angular6 with rxjs6 and I guess all is changed ;)
I want to know, how I could propagate an Error-Object within map of an observable to the subscribe OnError Part. I always end up in the OnNext Part.
Here is what I have so far:
Within a ng-component I have maybe the following method call
[...]
this.dataStreamService.execCall({ method : 'order_list',params : {}})
.subscribe( r => {
// here r provides the result data from http call
console.log("execCall result", r);
}, err => {
// HERE the "MAP ERROR OCCURED" Error should be occured as well,
// but in doesn't
console.log("execCall error",err);
});
[...]
The called service method looks like:
execCall(dataStreamCall: DataStreamCall): Observable<DataStreamResult> {
let apiURL = '<some API-URL>';
let params = dataStreamCall.params;
// do HTTP request (this.http calls an extra service handler which wraps
// the angular httpClient and the API errors there
// There is NO Problem with that part :)
let apiResult = this.http.post(apiURL, params);
// Build a new Observable from type "DataStreamResult"
let dsr : Observable<DataStreamResult> = apiResult
.pipe(
map( httpresult => {
if (httpresult['status'] == false){
// the http call was basically successful,
// but state in data is false
// *** THIS IS NOT PROPAGATE TO SUBSCRIBE OnERROR ***
throwError({'msg' : 'MAP ERROR OCCURED'});
// also tried as alternative
return throwError({'msg' : 'MAP ERROR OCCURED'});
} else {
// here the http call was successful
let d = new DataStreamResult();
d.result = httpresult;
return d;
}
}),
catchError( err => {
// error is bubble up from http request handler
return throwError(err);
})
);
return dsr;
}
Finally the Question:
How could manage, that the "throwError" within the piped "map" is propagated to subscribe "err => { ... }".
The actual behavior for:
throwError({..})
I ended up in the subscribe OnNext Part with r = undefined
If I use:
return throwError({..})
I also ended up in the subscribe OnNext Part where r is the throwError-Observable
Thx in Advance
Best Regards
throwError({'msg' : 'MAP ERROR OCCURED'}) will return an observable that, when subscribed to, will effect an error notification. That is, it will call the subscriber's error method.
In your snippet, you either call throwError and ignore the value. Or you return its return value from a project function passed to the map operator.
Neither will effect an error.
There is no subscriber in the first situation, because the return value is ignored. And, in the second situation, there is no subscriber because the map operator doesn't subscribe to what it receives from the project function - the map operator's project function can return anything; it doesn't have to return an observable.
To throw an error within map, use:
throw {'msg' : 'MAP ERROR OCCURED'};
Related
I am looking for a way to overwrite expect method for TestController. My idea is existing tests whoever used t.expect method, I want to perform additional steps in those cases.
I came up with below sample code but testcafe runtime fails with below error
TypeError: Cannot read property '_expect$' of undefined
sample code attempting to override:
import { Selector } from "testcafe";
fixture`Getting Started`.page`http://devexpress.github.io/testcafe/example`;
test("My first test", async (t) => {
t = modify(t);
await t.typeText("#developer-name", "John Smith").click("#submit-button");
// Use the assertion to check if the actual header text is equal to the expected one
await t
.expect(Selector("#article-header").innerText)
.eql("Thank you, John Smith!");
});
function modify(t) {
let prevExpect = t.expect;
t.expect = (param) => {
console.log("modified expecte has been used");
return prevExpect(param);
};
return t;
}
Also, when using t.click(Selector(...).expect(...), It doesn't use my overwritten expect. How to make it work in the call chain as well?
Technically, it's possible to overwrite the expect method, but please note that this approach may lead to incorrect work and unexpected errors.
You need to modify your modify function as follows:
function modify (t) {
let prevExpect = t.expect;
t.expect = (param) => {
console.log("modified expect has been used");
return prevExpect.call(t, param);
};
return t;
}
As for the t.click(Selector(...).expect(...) issue, you call the expect method of Selector, but Selector does not have the expect method.
You need to add ) after Selector:
await t.click(Selector(...)).expect(...)
I have a pretty simple http call happening. Upon error I want the request to retry 3 times with a three second delay between retries. I have worked out a solution that is close:
return this.http.put<string>(URL,
value).retryWhen(err => {
return err
.delay(3000)
.take(3)
.concat(Observable.throw("error occurred"));
})
.catch(err => this.handleHttpError(err)) ;
The client retries three times like I expect. However, I don't know how to throw the final error in such a way that my error handler (which normally expects an HttpResponse argument) can process the error.
Once I have taken(3) how can I get the final error, and convert it to an HttpResponse to send to my handler? Or am I looking at this the wrong way?
I need to know, at the end of the day, the HttpResponse that accompanied the error(s). When I throw the err from the retryWhen at the concat function that doesn't seem to accomplish it.
I am betting this is a pretty common thing to do, but being newer to Angular 5 and react I think I am just missing the boat.
You can use concatMap to count how many times you've tried to resubscribe and according to that send next or error notifications (this means re-throwing the error in the inner observable that is propagated further).
Observable.throw(42)
.retryWhen(err => err
.do(console.info) // remove, prints 42
.delay(3000)
.concatMap((error, index) => {
if (index === 2) {
return Observable.throw("error occurred"); // or Observable.throw(error);
}
return Observable.of(null);
})
)
// .catch(err => handleHttpError(err))
.subscribe(
v => console.log('next', v), // not called
e => console.log('error handler', e),
);
This prints the following output:
42
42
42
error handler: error occurred
See live demo: https://stackblitz.com/edit/rxjs5-jt5ald
For anyone who runs into this, I was able to capture the httpErrorResponse by slightly changing the return:
first I added a local var
let httpError: HttpErrorResponse = null;
then I modified the return:
return error
.do(err => {if (err instanceof HttpErrorResponse) {httpError = err; }})
.delay(3000)
.take(5)
.concat(Observable.throw(error));
})
this allows me to cache the last http error response. I then look for this in the catch and work accordingly. Seems to be working just fine.
I'm trying to understand an article here, and now everything is clear but one code fragment, mentioned on pre-last code block, with a total of 1 to 17 lines, and this fragment is from line 1 to 9:
app.use(function(req,res,next) {
redis.get(req.user.email, function(err, id) {
if (err) next(err);
req.emitToUser = function() {
var soc = id && io.to(id);
soc.emit.apply(soc, arguments);
}
});
});
and I think its some shortcomings in my javascript knowledge are the root cause.
My knowledge over this code fragment:
The 'apply' method will execute the 'emit' with 'soc' as 'this' value
and feeds the 'emit' method with 'arguments' (am I right here
please?)
socket.id is related to the email of socket owner, because id.to(id) is based on the fact that the socket.id is the room where every socket is joined with itself. Redis provides the key-value data structure that holds user email as key, and the value is the socket.id.
problems:
where 'arguments' is coming from?
what's the purpose of this code fragment?
Please make me clear.
There are some issues with this code, but the general idea is to define a method on the req object req.emitToUser() for every incoming request that will allow some other route handler later in the chain to use that method to emit to the user who make the request. This is a common desire to want to connect a currently connected socket.io connection to the user making the http request.
Let's look at each line here:
redis.get(req.user.email, function(err, id) {
Look up the req.user.email in the redis database to get a socket.io id associated with that email that has previously been saved in that redis database.
if (err) next(err);
If it wasn't found in redis, make this request fail with an error.
req.emitToUser = function() {
Assign a new method to the current req object so that other route handlers later in the chain can use that method.
var soc = id && io.to(id);
Look up the id value in socket.io to get the socket for that id. Technically io.to() doesn't return the socket, but it returns an object that you can call emit() on that will send to that socket.
soc.emit.apply(soc, arguments);
The role of soc.emit.apply(soc, arguments); is this:
Execute the soc.emit() method
Set the this value when executing that method to the soc object.
Set the arguments when executing that method to whatever the arguments were that were passed to req.emitToUser(x, y, z) when it was called.
Here's a more concrete example:
function fn(a, b, c) {
console.log(a, b, c);
}
fn.apply(null, [1, 2, 3]);
Using fn.apply(null, [1, 2, 3]); will be the same as:
fn(1, 2, 3);
Now, you'd likely never use .apply() in this exact way when the arguments are already known. The case for using it is when you have some arbitrary array that is passed to you (you don't know what's in it) and you want to pass those arguments along to some other function in the exact same order as they were given to you. That's what soc.emit.apply(soc, arguments); is doing. It's taking the arguments object (which is an array-like structure that represents the arguments that were passed to the parent function req.emitToUser() and passing those exact arguments on it sock.emit(). If you knew exactly how many arguments there would be, then you could hard-code that same code as this:
app.use(function(req,res,next) {
redis.get(req.user.email, function(err, id) {
if (err) next(err);
req.emitToUser = function(msg, data) {
var soc = id && io.to(id);
soc.emit(msg, data);
}
});
});
But, .apply() creates a more generic solution that will work regardless of how many arguments were passed to req.emitToUser() as it will just pass all the arguments on to soc.emit().
This line of code is a bit suspect:
var soc = id && io.to(id);
It appears to be trying to protect against there not being a proper id returned from redis earlier. But, if there's no id, then soc will not be a valid object and the following like of code:
soc.emit.apply(soc, arguments);
will throw. So, the id && io.to(id) isn't really providing the proper protection. It appears this should more likely be:
app.use(function(req,res,next) {
redis.get(req.user.email, function(err, id) {
if (err) next(err);
req.emitToUser = function() {
if (id) {
var soc = io.to(id);
soc.emit.apply(soc, arguments);
} else {
// not sure what you want here, perhaps return an error
// or throw a more meaningful exception
}
}
});
});
I've defined some API calls in Futures that make API calls to Mashery and Stripe
val stripeFuture = Future { // api call }
val masheryFuture = Future { //api call }
For the stripeFuture -The main logic is to set the stripeCustomerId on a Client object within the onSuccess block
stripeFuture onSuccess {
//client.stripeCustomerId
}
I've wrapped up the API calls in a for-comprehension similar to the example in Futures and Promises
val apiCalls = for {
masheryInfo <- masheryFuture
stripeCustomer <- stripeFuture
}
There is a rollback if one of the API calls fail
apiCalls onFailure {
case pse: MasheryException => {
// delete stripe customer id
}
case e: StripeException => {
//delete mashery api key
}
The problem is when the call to Mashery fails 'masheryFuture', I want to rollback 'get the stripe id' from the Client object but there is a around a 1 second delay til that call finishes and it doesn't set the stripeCustomerId until it hits the onSuccess block so within the ase pse: MasheryException => { } block, client.getstripeCustomerId returns null.
Is there a way of getting around this race condition for both of the API calls
Use Future.andThen.
The doc:
Applies the side-effecting function to the result of this future, and
returns a new future with the result of this future.
This method allows one to enforce that the callbacks are executed in a
specified order.
for (f <- Future(x).andThen { y }) etc.
Update:
for (f <- Future(x) andThen {
case Success(x) => use(x)
case _ => // ignore
}) yield result
I have a task to perform an HttpWebRequest using
Task<WebResponse>.Factory.FromAsync(req.BeginGetRespone, req.EndGetResponse)
which can obviously fail with a WebException. To the caller I want to return a Task<HttpResult> where HttpResult is a helper type to encapsulate the response (or not). In this case a 4xx or 5xx response is not an exception.
Therefore I've attached two continuations to the request task. One with TaskContinuationOptions OnlyOnRanToCompletion and the other with OnlyOnOnFaulted. And then wrapped the whole thing in a Task<HttpResult> to pick up the one result whichever continuation completes.
Each of the three child tasks (request plus two continuations) is created with the AttachedToParent option.
But when the caller waits on the returned outer task, an AggregateException is thrown is the request failed.
I want to, in the on faulted continuation, observe the WebException so the client code can just look at the result. Adding a Wait in the on fault continuation throws, but a try-catch around this doesn't help. Nor does looking at the Exception property (as section "Observing Exceptions By Using the Task.Exception Property" hints here).
I could install a UnobservedTaskException event handler to filter, but as the event offers no direct link to the faulted task this will likely interact outside this part of the application and is a case of a sledgehammer to crack a nut.
Given an instance of a faulted Task<T> is there any means of flagging it as "fault handled"?
Simplified code:
public static Task<HttpResult> Start(Uri url) {
var webReq = BuildHttpWebRequest(url);
var result = new HttpResult();
var taskOuter = Task<HttpResult>.Factory.StartNew(() => {
var tRequest = Task<WebResponse>.Factory.FromAsync(
webReq.BeginGetResponse,
webReq.EndGetResponse,
null, TaskCreationOptions.AttachedToParent);
var tError = tRequest.ContinueWith<HttpResult>(
t => HandleWebRequestError(t, result),
TaskContinuationOptions.AttachedToParent
|TaskContinuationOptions.OnlyOnFaulted);
var tSuccess = tRequest.ContinueWith<HttpResult>(
t => HandleWebRequestSuccess(t, result),
TaskContinuationOptions.AttachedToParent
|TaskContinuationOptions.OnlyOnRanToCompletion);
return result;
});
return taskOuter;
}
with:
private static HttpDownloaderResult HandleWebRequestError(
Task<WebResponse> respTask,
HttpResult result) {
Debug.Assert(respTask.Status == TaskStatus.Faulted);
Debug.Assert(respTask.Exception.InnerException is WebException);
// Try and observe the fault: Doesn't help.
try {
respTask.Wait();
} catch (AggregateException e) {
Log("HandleWebRequestError: waiting on antecedent task threw inner: "
+ e.InnerException.Message);
}
// ... populate result with details of the failure for the client ...
return result;
}
(HandleWebRequestSuccess will eventually spin off further tasks to get the content of the response...)
The client should be able to wait on the task and then look at its result, without it throwing due to a fault that is expected and already handled.
In the end I took the simplest route I could think of: hide the exception. This is possible because WebException has a property Response which gives access to the HttpWebResponse I want:
var requestTask = Task<WebResponse>.Factory.FromAsync(
webReq.BeginGetResponse,
ia => {
try {
return webReq.EndGetResponse(ia);
} catch (WebException exn) {
requestState.Log(...);
return exn.Response;
}
});
And then handle errors, redirects and success responses in the continuation task.