@@ -38,6 +38,11 @@ typedef RequestInterceptor(HttpResponseConfig);
38
38
typedef RequestErrorInterceptor (dynamic );
39
39
typedef Response (HttpResponse );
40
40
typedef ResponseError (dynamic );
41
+ typedef _CompleteResponse (HttpResponse );
42
+ typedef _RunCoaleced (fn ());
43
+
44
+ _runNow (fn ()) => fn ();
45
+ _identity (x) => x;
41
46
42
47
/**
43
48
* HttpInterceptors are used to modify the Http request. They can be added to
@@ -369,23 +374,29 @@ class HttpDefaults {
369
374
*/
370
375
@Injectable ()
371
376
class Http {
372
- var _pendingRequests = new HashMap <String , async.Future <HttpResponse >>();
373
- BrowserCookies _cookies;
374
- LocationWrapper _location;
375
- UrlRewriter _rewriter;
376
- HttpBackend _backend;
377
- HttpInterceptors _interceptors;
377
+ final _pendingRequests = new HashMap <String , async.Future <HttpResponse >>();
378
+ final BrowserCookies _cookies;
379
+ final LocationWrapper _location;
380
+ final UrlRewriter _rewriter;
381
+ final HttpBackend _backend;
382
+ final HttpInterceptors _interceptors;
383
+ final RootScope _rootScope;
384
+ final HttpConfig _httpConfig;
385
+ final VmTurnZone _zone;
386
+
387
+ final _responseQueue = < Function > [];
388
+ async .Timer _responseQueueTimer;
378
389
379
390
/**
380
391
* The defaults for [Http]
381
392
*/
382
- HttpDefaults defaults;
393
+ final HttpDefaults defaults;
383
394
384
395
/**
385
396
* Constructor, useful for DI.
386
397
*/
387
- Http (this ._cookies, this ._location, this ._rewriter, this ._backend,
388
- this .defaults , this ._interceptors );
398
+ Http (this ._cookies, this ._location, this ._rewriter, this ._backend, this .defaults,
399
+ this ._interceptors , this ._rootScope, this ._httpConfig, this ._zone );
389
400
390
401
/**
391
402
* Parse a [requestUrl] and determine whether this is a same-origin request as
@@ -481,29 +492,25 @@ class Http {
481
492
return new async .Future .value (new HttpResponse .copy (cachedResponse));
482
493
}
483
494
484
- var result = _backend.request (url,
485
- method: method,
486
- requestHeaders: config.headers,
487
- sendData: config.data,
488
- withCredentials: withCredentials).then ((dom.HttpRequest value) {
489
- // TODO: Uncomment after apps migrate off of this class.
490
- // assert(value.status >= 200 && value.status < 300);
491
-
492
- var response = new HttpResponse (value.status, value.responseText,
493
- parseHeaders (value), config);
494
-
495
- if (cache != null ) cache.put (url, response);
496
- _pendingRequests.remove (url);
497
- return response;
498
- }, onError: (error) {
499
- if (error is ! dom.ProgressEvent ) throw error;
500
- dom.ProgressEvent event = error;
501
- _pendingRequests.remove (url);
502
- dom.HttpRequest request = event.currentTarget;
503
- return new async .Future .error (
504
- new HttpResponse (request.status, request.response, parseHeaders (request), config));
505
- });
506
- return _pendingRequests[url] = result;
495
+ requestFromBackend (runCoalesced, onComplete, onError) => _backend.request (
496
+ url,
497
+ method: method,
498
+ requestHeaders: config.headers,
499
+ sendData: config.data,
500
+ withCredentials: withCredentials
501
+ ).then ((dom.HttpRequest req) => _onResponse (req, runCoalesced, onComplete, config, cache, url),
502
+ onError: (e) => _onError (e, runCoalesced, onError, config, url));
503
+
504
+ async .Future responseFuture;
505
+ if (_httpConfig.coalesceDuration != null ) {
506
+ async .Completer completer = new async .Completer ();
507
+ responseFuture = completer.future;
508
+ _zone.runOutsideAngular (() => requestFromBackend (
509
+ _coalesce, completer.complete, completer.completeError));
510
+ } else {
511
+ responseFuture = requestFromBackend (_runNow, _identity, _identity);
512
+ }
513
+ return _pendingRequests[url] = responseFuture;
507
514
};
508
515
509
516
var chain = [[serverRequest, null ]];
@@ -650,11 +657,50 @@ class Http {
650
657
xsrfCookieName: xsrfCookieName, interceptors: interceptors, cache: cache,
651
658
timeout: timeout);
652
659
660
+ _onResponse (dom.HttpRequest request, _RunCoaleced runCoalesced, _CompleteResponse onComplete,
661
+ HttpResponseConfig config, cache, String url) {
662
+ // TODO: Uncomment after apps migrate off of this class.
663
+ // assert(request.status >= 200 && request.status < 300);
664
+
665
+ var response = new HttpResponse (
666
+ request.status, request.responseText, parseHeaders (request), config);
667
+
668
+ if (cache != null ) cache.put (url, response);
669
+ _pendingRequests.remove (url);
670
+ return runCoalesced (() => onComplete (response));
671
+ }
672
+
673
+ _onError (error, _RunCoaleced runCoalesced, _CompleteResponse onError,
674
+ HttpResponseConfig config, String url) {
675
+ if (error is ! dom.ProgressEvent ) throw error;
676
+ dom.ProgressEvent event = error;
677
+ _pendingRequests.remove (url);
678
+ dom.HttpRequest request = event.currentTarget;
679
+ var response = new HttpResponse (
680
+ request.status, request.response, parseHeaders (request), config);
681
+ return runCoalesced (() => onError (new async .Future .error (response)));
682
+ }
683
+
684
+ _coalesce (fn ()) {
685
+ _responseQueue.add (fn);
686
+ if (_responseQueueTimer == null ) {
687
+ _responseQueueTimer = new async .Timer (_httpConfig.coalesceDuration, _flushResponseQueue);
688
+ }
689
+ }
690
+
691
+ _flushResponseQueue () => _zone.run (_flushResponseQueueSync);
692
+
693
+ _flushResponseQueueSync () {
694
+ _responseQueueTimer = null ;
695
+ _responseQueue.forEach (_runNow);
696
+ _responseQueue.clear ();
697
+ }
698
+
653
699
/**
654
700
* Parse raw headers into key-value object
655
701
*/
656
- static Map <String , String > parseHeaders (dom.HttpRequest value ) {
657
- var headers = value .getAllResponseHeaders ();
702
+ static Map <String , String > parseHeaders (dom.HttpRequest request ) {
703
+ var headers = request .getAllResponseHeaders ();
658
704
659
705
var parsed = new HashMap ();
660
706
@@ -704,3 +750,10 @@ class Http {
704
750
.replaceAll ('%2C' , ',' )
705
751
.replaceAll ('%20' , pctEncodeSpaces ? '%20' : '+' );
706
752
}
753
+
754
+ @Injectable ()
755
+ class HttpConfig {
756
+ final Duration coalesceDuration;
757
+
758
+ HttpConfig ({this .coalesceDuration});
759
+ }
0 commit comments