import retry from './retry.js'
import initialParams from './internal/initialParams.js'
import {default as wrapAsync, isAsync} from './internal/wrapAsync.js'
import { promiseCallback, PROMISE_SYMBOL } from './internal/promiseCallback.js'
/**
* A close relative of [`retry`]{@link module:ControlFlow.retry}. This method
* wraps a task and makes it retryable, rather than immediately calling it
* with retries.
*
* @name retryable
* @static
* @memberOf module:ControlFlow
* @method
* @see [async.retry]{@link module:ControlFlow.retry}
* @category Control Flow
* @param {Object|number} [opts = {times: 5, interval: 0}| 5] - optional
* options, exactly the same as from `retry`, except for a `opts.arity` that
* is the arity of the `task` function, defaulting to `task.length`
* @param {AsyncFunction} task - the asynchronous function to wrap.
* This function will be passed any arguments passed to the returned wrapper.
* Invoked with (...args, callback).
* @returns {AsyncFunction} The wrapped function, which when invoked, will
* retry on an error, based on the parameters specified in `opts`.
* This function will accept the same parameters as `task`.
* @example
*
* async.auto({
* dep1: async.retryable(3, getFromFlakyService),
* process: ["dep1", async.retryable(3, function (results, cb) {
* maybeProcessData(results.dep1, cb);
* })]
* }, callback);
*/
export default function retryable (opts, task) {
if (!task) {
task = opts;
opts = null;
}
let arity = (opts && opts.arity) || task.length
if (isAsync(task)) {
arity += 1
}
var _task = wrapAsync(task);
return initialParams((args, callback) => {
if (args.length < arity - 1 || callback == null) {
args.push(callback)
callback = promiseCallback()
}
function taskFn(cb) {
_task(...args, cb);
}
if (opts) retry(opts, taskFn, callback);
else retry(taskFn, callback);
return callback[PROMISE_SYMBOL]
});
}