@@ -55,6 +55,7 @@ export class HubConnection {
5555 private _connectionStarted : boolean ;
5656 private _startPromise ?: Promise < void > ;
5757 private _stopPromise ?: Promise < void > ;
58+ private _nextKeepAlive : number = 0 ;
5859
5960 // The type of these a) doesn't matter and b) varies when building in browser and node contexts
6061 // Since we're building the WebPack bundle directly from the TypeScript, this matters (previously
@@ -74,6 +75,8 @@ export class HubConnection {
7475 *
7576 * The default value is 15,000 milliseconds (15 seconds).
7677 * Allows the server to detect hard disconnects (like when a client unplugs their computer).
78+ * The ping will happen at most as often as the server pings.
79+ * If the server pings every 5 seconds, a value lower than 5 will ping every 5 seconds.
7780 */
7881 public keepAliveIntervalInMilliseconds : number ;
7982
@@ -604,24 +607,39 @@ export class HubConnection {
604607 return ;
605608 }
606609
610+ // Set the time we want the next keep alive to be sent
611+ // Timer will be setup on next message receive
612+ this . _nextKeepAlive = new Date ( ) . getTime ( ) + this . keepAliveIntervalInMilliseconds ;
613+
607614 this . _cleanupPingTimer ( ) ;
608- this . _pingServerHandle = setTimeout ( async ( ) => {
609- if ( this . _connectionState === HubConnectionState . Connected ) {
610- try {
611- await this . _sendMessage ( this . _cachedPingMessage ) ;
612- } catch {
613- // We don't care about the error. It should be seen elsewhere in the client.
614- // The connection is probably in a bad or closed state now, cleanup the timer so it stops triggering
615- this . _cleanupPingTimer ( ) ;
616- }
617- }
618- } , this . keepAliveIntervalInMilliseconds ) ;
619615 }
620616
621617 private _resetTimeoutPeriod ( ) {
622618 if ( ! this . connection . features || ! this . connection . features . inherentKeepAlive ) {
623619 // Set the timeout timer
624620 this . _timeoutHandle = setTimeout ( ( ) => this . serverTimeout ( ) , this . serverTimeoutInMilliseconds ) ;
621+
622+ // Set keepAlive timer if there isn't one
623+ if ( this . _pingServerHandle === undefined )
624+ {
625+ let nextPing = this . _nextKeepAlive - new Date ( ) . getTime ( ) ;
626+ if ( nextPing < 0 ) {
627+ nextPing = 0 ;
628+ }
629+
630+ // The timer needs to be set from a networking callback to avoid Chrome timer throttling from causing timers to run once a minute
631+ this . _pingServerHandle = setTimeout ( async ( ) => {
632+ if ( this . _connectionState === HubConnectionState . Connected ) {
633+ try {
634+ await this . _sendMessage ( this . _cachedPingMessage ) ;
635+ } catch {
636+ // We don't care about the error. It should be seen elsewhere in the client.
637+ // The connection is probably in a bad or closed state now, cleanup the timer so it stops triggering
638+ this . _cleanupPingTimer ( ) ;
639+ }
640+ }
641+ } , nextPing ) ;
642+ }
625643 }
626644 }
627645
@@ -813,6 +831,7 @@ export class HubConnection {
813831 private _cleanupPingTimer ( ) : void {
814832 if ( this . _pingServerHandle ) {
815833 clearTimeout ( this . _pingServerHandle ) ;
834+ this . _pingServerHandle = undefined ;
816835 }
817836 }
818837
0 commit comments