3

I found this answer where the solution proposed is to use Observables to set http requests' timeout.

However my code is structured to use mostly promises (I use observables where I want to have data automatically updated - that's not the case of the API calls).

Here is my code (inspired by Angular 2 tutorial):

makePostRequest(requestUrl:string, requestBody: any, requestOptions?: RequestOptions): Promise<any> {
    requestOptions = requestOptions || new RequestOptions({ headers: this._defaultHeaders });
    return this._http.post(requestUrl, JSON.stringify(requestBody), requestOptions)
        .toPromise()
        .then(this.extractData)
        .catch(this.handleError)
}

How to set a timeout and throw an error (if the timeout expires) that I then catch in .catch() or - alternatively - replicate the exact precise behavior with Observables (including converting the result to a Promise and not monitoring for monitoring for API update(*))?

(*) NOTE: I'm not sure whether Observables keep calling the APIs to check for new data, but that's not the point of my question, I just want to make sure this behavior does not occur.

Community
  • 1
  • 1
dragonmnl
  • 14,578
  • 33
  • 84
  • 129

3 Answers3

3

I would expect this to do what you want (not tried):

makePostRequest(requestUrl:string, requestBody: any, requestOptions?: RequestOptions): Promise<any> {
    requestOptions = requestOptions || new RequestOptions({ headers: this._defaultHeaders });
    return this._http.post(requestUrl, JSON.stringify(requestBody), requestOptions)
        .timeout(3000, new Error('timeout exceeded'))
        .toPromise()
        .then(this.extractData)
        .catch(this.handleError)
}

From Angular2 timeout in http post request

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • already tried that. apparently timeout is supported only by observables – dragonmnl Jun 16 '16 at 16:42
  • And that doesn't work? Here is explained how to timeout a Promise http://stackoverflow.com/questions/32461271/nodejs-timeout-a-promise-if-failed-to-complete-in-time I don't know how to use it in your case because you aren't creating the `Promise`. You would need to do something like `return new Promise((resolve, reject) => { http.post().subscribe(... resolve(), error => reject())})` – Günter Zöchbauer Jun 16 '16 at 16:46
  • I don't remember the exact error in this specific case but I remember the cause : timeout is valid only in a observable chain, without promises. – dragonmnl Jun 16 '16 at 16:50
  • I'll make some other try next days. for now I'll up vote for the effort :) – dragonmnl Jun 16 '16 at 16:50
  • I found a solution (I answer to my own question). your answer was almost correct. But the right requence is first map, then toPromise and finally catch, taking care of importing the necessary rxjs operators. – dragonmnl Jun 17 '16 at 12:32
2

The solution (right chain + imports) I found:

// ! must import these
...
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';

return this._http.get(requestUrl, requestOptions)
        .timeout(5000, new Error( 'HTTP (GET) timeout for path: ' + requestUrl))
        .map(this.extractData)
        .toPromise()
        .catch(this.handleError);
dragonmnl
  • 14,578
  • 33
  • 84
  • 129
0

I approached this a bit differently. I had logic that relied on a promise being returned—and doing .timeout caused it to immediately fail regardless of the timeout duration.

My solution was to create a new promise instead of using toPromise:

const timeoutInMs = 3000;

const request = this._http
    .post(/* ... */)
    .timeout(timeoutInMs);

return new Promise((resolve, reject) => {
    request
        .take(1)
        .subscribe(
            data => resolve(data),
            error => reject(error),
        );
});

If you use this a lot, you could refactor it into a function (not yet tested):

const toPromiseWithTimeout = <T>(obs: Observable<T>, ms): Promise<T> =>
    new Promise<T>((resolve, reject) => {
        obs
            .timeout(ms)
            .take(1)
            .subscribe(
                data => resolve(data),
                error => reject(error),
            );
    });

And to use it:

const timeoutInMs = 3000;

const request = this._http
    .post<ResponseType>(/* ... */);

return toPromiseWithTimeout(request, timeoutInMs);
Jordan Gray
  • 16,306
  • 3
  • 53
  • 69