AutoRetry delay implementation
parent
ca5d064455
commit
af4a775155
|
@ -40,10 +40,10 @@ module.exports = Trait( 'AutoRetry' )
|
|||
'private _tries': 0,
|
||||
|
||||
/**
|
||||
* Delay in milliseconds before making the nth request as a function
|
||||
* of n
|
||||
* Function to be passed a continuation to introduce a delay between
|
||||
* requests
|
||||
*
|
||||
* @var {function(number): number}
|
||||
* @var {function(number,function(),function())} delay
|
||||
*/
|
||||
'private _delay': null,
|
||||
|
||||
|
@ -52,33 +52,42 @@ module.exports = Trait( 'AutoRetry' )
|
|||
* Initialize auto-retry
|
||||
*
|
||||
* If TRIES is negative, then requests will continue indefinitely until
|
||||
* one succeeds. If TRIES is 0, then no requests will be performed.
|
||||
* one succeeds or is aborted by DELAY. If TRIES is 0, then no requests
|
||||
* will be performed.
|
||||
*
|
||||
* @param {function(?Error,*): boolean} pred predicate determining if
|
||||
* a retry is needed
|
||||
* @param {number} tries maximum number of tries,
|
||||
* including the initial
|
||||
* request
|
||||
* @param {function(number): number} delay delay in milliseconds before
|
||||
* making the nth request as
|
||||
* a function of n
|
||||
* If DELAY is a function, then it invoked with a retry continuation
|
||||
* before each retry, the number of tries remaining, and a failure
|
||||
* continuation that may be used to abort the process at an arbitrary
|
||||
* time.
|
||||
*
|
||||
* @return {undefined}
|
||||
*/
|
||||
* @param {function(?Error,*): boolean} pred predicate determining if
|
||||
* a retry is needed
|
||||
* @param {number} tries maximum number of tries,
|
||||
* including the initial
|
||||
* request
|
||||
*
|
||||
* @param {?function(number,function(),function())} delay
|
||||
* an optional function
|
||||
* accepting a continuation
|
||||
* to continue with the next
|
||||
* request
|
||||
*
|
||||
* @return {undefined}
|
||||
*/
|
||||
__mixin: function( pred, tries, delay )
|
||||
{
|
||||
if ( typeof pred !== 'function' )
|
||||
{
|
||||
throw TypeError( 'Predicate must be a function' );
|
||||
}
|
||||
if ( typeof delay !== 'function' )
|
||||
if ( delay && ( typeof delay !== 'function' ) )
|
||||
{
|
||||
throw TypeError( "Delay must be a function" );
|
||||
}
|
||||
|
||||
this._pred = pred;
|
||||
this._tries = +tries;
|
||||
this._delay = delay;
|
||||
this._delay = delay || function( _, c ) { c(); };
|
||||
},
|
||||
|
||||
|
||||
|
@ -96,9 +105,8 @@ module.exports = Trait( 'AutoRetry' )
|
|||
* data from the final request are returned.
|
||||
*
|
||||
* If the number of tries is negative, then requests will be performed
|
||||
* indefinitely until success.
|
||||
*
|
||||
* TODO: A means of aborting.
|
||||
* indefinitely until success; the delay function (as provided via the
|
||||
* constructor) may be used to abort in this case.
|
||||
*
|
||||
* @param {string} input binary data to transmit
|
||||
* @param {function(?Error,*)} callback continuation upon reply
|
||||
|
@ -144,14 +152,26 @@ module.exports = Trait( 'AutoRetry' )
|
|||
return _self._succeed( output, callback );
|
||||
}
|
||||
|
||||
var fail = function()
|
||||
{
|
||||
_self._fail( err, output, callback );
|
||||
};
|
||||
|
||||
// note that we intentionally do not want to check <= 1, so that
|
||||
// we can proceed indefinitely (JavaScript does not wrap on overflow)
|
||||
if ( n === 1 )
|
||||
{
|
||||
return _self._fail( err, output, callback );
|
||||
return fail();
|
||||
}
|
||||
|
||||
_self._try( input, callback, ( n - 1 ) );
|
||||
_self._delay(
|
||||
( n - 1 ),
|
||||
function()
|
||||
{
|
||||
_self._try( input, callback, ( n - 1 ) );
|
||||
},
|
||||
fail
|
||||
);
|
||||
} );
|
||||
},
|
||||
|
||||
|
@ -184,4 +204,3 @@ module.exports = Trait( 'AutoRetry' )
|
|||
callback( err, output );
|
||||
},
|
||||
} );
|
||||
|
||||
|
|
|
@ -194,4 +194,3 @@ module.exports = Class( 'XhrHttpImpl' )
|
|||
);
|
||||
}
|
||||
} );
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ describe( 'dapi.AutoRetry trait', function()
|
|||
|
||||
// success (but note the number of retries presented)
|
||||
var stub = _createStub( null, '' )
|
||||
.use( Sut( _void, 5, _void ) )
|
||||
.use( Sut( _void, 5 ) )
|
||||
();
|
||||
|
||||
stub.request( given, function()
|
||||
|
@ -62,7 +62,7 @@ describe( 'dapi.AutoRetry trait', function()
|
|||
// notice that we provide an error to the stub; this will ensure
|
||||
// that the returned error is null even when one is provided
|
||||
var stub = _createStub( {}, chk )
|
||||
.use( Sut( _void, 1, _void ) )
|
||||
.use( Sut( _void, 1 ) )
|
||||
();
|
||||
|
||||
stub.request( '', function( err, data )
|
||||
|
@ -85,7 +85,7 @@ describe( 'dapi.AutoRetry trait', function()
|
|||
var n = 5;
|
||||
|
||||
var stub = _createStub( {}, {} )
|
||||
.use( Sut( _true, n, _void ) )
|
||||
.use( Sut( _true, n ) )
|
||||
();
|
||||
|
||||
stub.request( {}, function( err, _ )
|
||||
|
@ -104,7 +104,7 @@ describe( 'dapi.AutoRetry trait', function()
|
|||
// XXX: this does not test for most recent, because the return
|
||||
// data are static for each request
|
||||
var stub = _createStub( e, output )
|
||||
.use( Sut( _true, 1, _void ) )
|
||||
.use( Sut( _true, 1 ) )
|
||||
();
|
||||
|
||||
stub.request( {}, function( err, data )
|
||||
|
@ -127,7 +127,7 @@ describe( 'dapi.AutoRetry trait', function()
|
|||
};
|
||||
|
||||
var stub = _createStub()
|
||||
.use( Sut( pred, -1, _void ) )
|
||||
.use( Sut( pred, -1 ) )
|
||||
();
|
||||
|
||||
stub.request( {}, function( _, __ )
|
||||
|
@ -145,7 +145,7 @@ describe( 'dapi.AutoRetry trait', function()
|
|||
it( 'will perform zero requests with null results', function( done )
|
||||
{
|
||||
var stub = _createStub( {}, {} )
|
||||
.use( Sut( _void, 0, _void ) )
|
||||
.use( Sut( _void, 0 ) )
|
||||
();
|
||||
|
||||
stub.request( {}, function( err, data )
|
||||
|
@ -157,6 +157,114 @@ describe( 'dapi.AutoRetry trait', function()
|
|||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
describe( 'when a delay function is provided', function()
|
||||
{
|
||||
it( 'will wait for continuation before retry', function( done )
|
||||
{
|
||||
var waited = false;
|
||||
|
||||
var wait = function( _, c )
|
||||
{
|
||||
waited = true;
|
||||
c();
|
||||
};
|
||||
|
||||
var stub = _createStub( {}, {} )
|
||||
.use( Sut( _true, 2, wait ) )
|
||||
();
|
||||
|
||||
stub.request( {}, function( _, __ )
|
||||
{
|
||||
expect( waited ).to.equal( true );
|
||||
done();
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
it( 'will not process if continuation is not called', function()
|
||||
{
|
||||
var waited = false;
|
||||
var wait = function( _, c )
|
||||
{
|
||||
waited = true;
|
||||
/* do not invoke */
|
||||
};
|
||||
|
||||
var stub = _createStub( {}, {} )
|
||||
.use( Sut( _true, 2, wait ) )
|
||||
();
|
||||
|
||||
// this works because we know that our stub is invoked
|
||||
// synchronously
|
||||
stub.request( {}, function( _, __ )
|
||||
{
|
||||
throw Error( "Should not have been called!" );
|
||||
} );
|
||||
|
||||
expect( waited ).to.equal( true );
|
||||
} );
|
||||
|
||||
|
||||
it( 'will call delay function until success', function()
|
||||
{
|
||||
var n = 5;
|
||||
var wait = function( tries_left, c )
|
||||
{
|
||||
n--;
|
||||
|
||||
// the first argument is the number of tries left
|
||||
expect( tries_left ).to.equal( n );
|
||||
c();
|
||||
};
|
||||
|
||||
var pred = function()
|
||||
{
|
||||
return n > 0;
|
||||
};
|
||||
|
||||
var stub = _createStub( {}, {} )
|
||||
.use( Sut( pred, n, wait ) )
|
||||
();
|
||||
|
||||
// this works because we know that our stub is invoked
|
||||
// synchronously
|
||||
stub.request( {}, _void );
|
||||
|
||||
// the first request counts as one, which brings us down to 4,
|
||||
// but the wait function has not been called at this point; so,
|
||||
// we expect that it will only be called four times
|
||||
expect( n ).to.equal( 1 );
|
||||
} );
|
||||
|
||||
|
||||
it( 'allows aborting via failure continuation', function( done )
|
||||
{
|
||||
var err_expect = {},
|
||||
out_expect = [];
|
||||
|
||||
var wait = function( _, __, abort )
|
||||
{
|
||||
abort();
|
||||
};
|
||||
|
||||
// without aborting, this would never finish
|
||||
var stub = _createStub( err_expect, out_expect )
|
||||
.use( Sut( _true, -1, wait ) )
|
||||
();
|
||||
|
||||
// this works because we know that our stub is invoked
|
||||
// synchronously
|
||||
stub.request( {}, function( err, output )
|
||||
{
|
||||
expect( err ).to.equal( err_expect );
|
||||
expect( output ).to.equal( out_expect );
|
||||
|
||||
done();
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue