Reputation: 2357
I'm using Angular's HttpClient to make HTTP requests and I'd like to specify a timeout for them.
I know I can use HTTPInterceptors and add a timeout to RxJS operators, however, these apply to the whole request which I don't want to abort if data transfer is in progress, only if the browser is hanging while trying to connect.
The kind of timeout I need is available in Node.js for example which is well explained here:
Let's say you called
socket.setTimeout(300)
to set the timeout as 300 ms, and it took 100 ms for DNS lookup, 100 ms for making a connection with a remote server, 200 ms for the remote server to send response headers, 50 ms for transferring the first half of the response body and another 50 ms for the rest. While the entire request & response took more than 500 ms, timeout event is not emitted at all.
Is it possible to have a timeout like this in an Angular app?
Upvotes: 4
Views: 2140
Reputation: 316
For those still seeking a 'hacky' solution you can create an observable and insert an empty/failure after your desired timeout period:
handleError(error: HttpErrorResponse) {
console.warn('HTTPErrorResponse caught', error);
return observableOf({});
}
async __sendCommandHTTP(cmd: SoftAPCommand) {
const URL = this.options.host + cmd.name;
let result: SoftAPResponse = {
name: cmd.name,
payload: {},
error: false,
};
this.logger.debug('[softap-setup starting request');
await new Promise(resolve => {
const httpEvent: Subject<any> = new Subject<any>();
let returned = false;
const sub = this.http
.get<any>(URL, {})
.pipe(catchError(this.handleError))
.subscribe(data => httpEvent.next(data));
// Our cheeky method to ensure a timeout
setTimeout(async () => {
if (!returned) {
this.logger.info('[softap-setup] timeout on ' + result.name);
httpEvent.next({});
}
}, 5000);
httpEvent.subscribe(data => {
this.logger.info('[softap-setup] response ', data);
returned = true;
switch (cmd.name) {
case 'scan-ap':
if (Object.prototype.hasOwnProperty.call(data, 'scans') && data.scans.length) {
result.payload = data.scans;
} else {
result.error = true;
}
break;
default:
result.payload = data;
break;
}
httpEvent.complete();
resolve();
});
});
return result;
}
Basically either the response or the timeout flags there has been a result. The handleError function also neatly handles any eventual errors that may come along, ie the host isn't available. You could apply other logic in there or even pass along the HTTPErrorResponse object.
Upvotes: 0
Reputation: 30088
I looked at the source code for the HttpClient. The code that actually deals with the underlying XMLHttpRequest is the class HttpXhrBackend, in source file named xhr.ts
Unfortunately, HttpXhrBackend just uses the default settings of XMLHttpRequest, and does not provide a way to set the XMLHttpRequest's timeout value.
I have seen suggestions for using RxJS operators to shorten the effective timeout, but that's a bit of a hack, and doesn't really do what you are asking for.
So, technically, the answer to your question is "No", not with the stock Angular HttpClient, but I suppose that you could create your own implementation of HttpBackend and attempt to inject that.
P.S. This article shows how to provide a custom HttpBackend implementation.
Upvotes: 4