@@ -54,6 +54,7 @@ export class HubConnection {
5454 private connectionStarted : boolean ;
5555 private startPromise ?: Promise < void > ;
5656 private stopPromise ?: Promise < void > ;
57+ private nextKeepAlive : number = 0 ;
5758
5859 // The type of these a) doesn't matter and b) varies when building in browser and node contexts
5960 // Since we're building the WebPack bundle directly from the TypeScript, this matters (previously
@@ -73,6 +74,8 @@ export class HubConnection {
7374 *
7475 * The default value is 15,000 milliseconds (15 seconds).
7576 * Allows the server to detect hard disconnects (like when a client unplugs their computer).
77+ * The ping will happen at most as often as the server pings.
78+ * If the server pings every 5 seconds, a value lower than 5 will ping every 5 seconds.
7679 */
7780 public keepAliveIntervalInMilliseconds : number ;
7881
@@ -599,24 +602,42 @@ export class HubConnection {
599602 }
600603
601604 private resetKeepAliveInterval ( ) {
605+ if ( this . connection . features . inherentKeepAlive ) {
606+ return ;
607+ }
608+
609+ // Set the time we want the next keep alive to be sent
610+ // Timer will be setup on next message receive
611+ this . nextKeepAlive = new Date ( ) . getTime ( ) + this . keepAliveIntervalInMilliseconds ;
612+
602613 this . cleanupPingTimer ( ) ;
603- this . pingServerHandle = setTimeout ( async ( ) => {
604- if ( this . connectionState === HubConnectionState . Connected ) {
605- try {
606- await this . sendMessage ( this . cachedPingMessage ) ;
607- } catch {
608- // We don't care about the error. It should be seen elsewhere in the client.
609- // The connection is probably in a bad or closed state now, cleanup the timer so it stops triggering
610- this . cleanupPingTimer ( ) ;
611- }
612- }
613- } , this . keepAliveIntervalInMilliseconds ) ;
614614 }
615615
616616 private resetTimeoutPeriod ( ) {
617617 if ( ! this . connection . features || ! this . connection . features . inherentKeepAlive ) {
618618 // Set the timeout timer
619619 this . timeoutHandle = setTimeout ( ( ) => this . serverTimeout ( ) , this . serverTimeoutInMilliseconds ) ;
620+
621+ // Set keepAlive timer if there isn't one
622+ if ( this . pingServerHandle === undefined ) {
623+ let nextPing = this . nextKeepAlive - new Date ( ) . getTime ( ) ;
624+ if ( nextPing < 0 ) {
625+ nextPing = 0 ;
626+ }
627+
628+ // The timer needs to be set from a networking callback to avoid Chrome timer throttling from causing timers to run once a minute
629+ this . pingServerHandle = setTimeout ( async ( ) => {
630+ if ( this . connectionState === HubConnectionState . Connected ) {
631+ try {
632+ await this . sendMessage ( this . cachedPingMessage ) ;
633+ } catch {
634+ // We don't care about the error. It should be seen elsewhere in the client.
635+ // The connection is probably in a bad or closed state now, cleanup the timer so it stops triggering
636+ this . cleanupPingTimer ( ) ;
637+ }
638+ }
639+ } , nextPing ) ;
640+ }
620641 }
621642 }
622643
@@ -807,6 +828,7 @@ export class HubConnection {
807828 private cleanupPingTimer ( ) : void {
808829 if ( this . pingServerHandle ) {
809830 clearTimeout ( this . pingServerHandle ) ;
831+ this . pingServerHandle = undefined ;
810832 }
811833 }
812834
0 commit comments