function whileAsync( predicate, fcn, done, thisArg ) {
	var args;
	var idx;
	if ( !isFunction( predicate ) ) {
		throw new TypeError( format( 'invalid argument. First argument must be a function. Value: `%s`.', predicate ) );
	}
	if ( !isFunction( fcn ) ) {
		throw new TypeError( format( 'invalid argument. Second argument must be a function. Value: `%s`.', fcn ) );
	}
	if ( !isFunction( done ) ) {
		throw new TypeError( format( 'invalid argument. Third argument must be a function. Value: `%s`.', done ) );
	}
	args = [];
	idx = 0;
	predicate( idx, onPredicate );

	/**
	* Callback invoked upon a predicate result.
	*
	* @private
	* @param {(Error|null)} error - error object
	* @param {boolean} result - predicate result
	* @returns {void}
	*/
	function onPredicate( error, result ) {
		if ( error ) {
			return done( error );
		}
		if ( result ) {
			return fcn.call( thisArg, idx, next );
		}
		if ( args.length ) {
			args.unshift( null ); // error argument
		}
		done.apply( null, args );
	}

	/**
	* Callback invoked upon completion of a provided function.
	*
	* @private
	* @param {(Error|null)} error - error object
	* @param {...*} results - function results
	* @returns {void}
	*/
	function next( error ) {
		var i;
		if ( error ) {
			return done( error );
		}
		idx += 1;

		// Cache the most recent results...
		if ( arguments.length > 1 ) {
			args = new Array( arguments.length-1 );
			for ( i = 1; i < arguments.length; i++ ) {
				args[ i-1 ] = arguments[ i ];
			}
		}
		// Run the test condition:
		predicate( idx, onPredicate );
	}
}
Invokes a function while a test condition is true.

@param {Function} predicate - function which indicates whether to continue invoking a function
@param {Function} fcn - function to invoke
@param {Callback} done - callback to invoke upon completion
@param {*} [thisArg] - execution context for the invoked function
@throws {TypeError} first argument must be a function
@throws {TypeError} second argument must be a function
@throws {TypeError} third argument must be a function

@example
function predicate( i, clbk ) {
    clbk( null, i < 5 );
}

function fcn( i, next ) {
    setTimeout( onTimeout, i );
    function onTimeout() {
        console.log( 'beep: %d', i );
        next();
    }
}

function done( error ) {
    if ( error ) {
        throw error;
    }
}

whileAsync( predicate, fcn, done );