I want to implement a recursive serial call to the promise method times
, returning the result of the fn
function of calling N times to the array.
At present, I added an additional attribute results
to the times
function to save the results of each fn
call.
I also don’t want to use module-scoped variables to save the results. Or, save the results by passing additional parameters like times(fn, n, results)
, it will break the function signature.
async/await
syntax is not allowed.
Is there any way to only use function local variables to save the result?
const times = Object.assign(
(fn: (...args: any) => Promise<any>, n: number = 1) => {
if (n === 0) return times.results;
return fn().then((res) => {
times.results.push(res);
return times(fn, --n);
});
},
{ results: [] as any[] },
);
Usage:
const createPromise = (args: any) =>
new Promise((resolve) => {
setTimeout(() => {
console.log(`[${new Date().toISOString()}]args: `, args);
resolve(args);
}, 1000);
});
async function test() {
const actual = await times(() => asyncFn('data'), 3);
console.log(actual); // output: [ 'data', 'data', 'data' ]
}
CodePudding user response:
There's no reason to use a stateful result
variable at all. Just invert the recursion direction:
function times<T>(fn: () => Promise<T>, n: number = 1): Promise<T[]> {
if (n === 0) return Promise.resolve([]);
else return times(fn, n-1).then(results =>
fn().then(res => {
results.push(res);
return results;
})
});
}
But if you insist on creating promises more gradually, you'll need to use an accumulator parameter:
function times<T>(fn: () => Promise<T>, n: number = 1, results: T[] = []): Promise<T[]> {
if (n === 0) return Promise.resolve(results);
else return fn().then(res => {
results.push(res);
return times(fn, n-1, results);
});
}
If you don't like having that additional optional parameter in your signature, use a local helper function that does the recursion:
function times<T>(fn: () => Promise<T>, n: number = 1): Promise<T[]> {
function recurse(n: number, results: T[]): Promise<T[]> {
if (n === 0) return Promise.resolve(results);
else return fn().then(res => {
results.push(res);
return recurse(n-1, results);
});
}
return recurse(n, []);
}
// or more ugly (stateful):
function times<T>(fn: () => Promise<T>, n: number = 1): Promise<T[]> {
let results: T[] = [];
function recurse(): Promise<T[]> {
if (n-- === 0) return Promise.resolve(results);
else return fn().then(res => {
results.push(res);
return recurse();
});
}
return recurse();
}
CodePudding user response:
What you are trying to achieve is to have a sequential and a recursive pattern. You can achieve it as following
Solution:
- In your function create 2 promises
finalPromise
: Promise to be returned from functionpromise
: Promise that will be created by passedfn
- For
finalPromise
, copy reference of resolver function and store it for manual call, sayresolverFn
- Create a variable
result
to store values. This should be of typeany[]
- On
promise.then
,- Push received value in
result
- Make subsequent call to times.
- Push received value in
- Trick for subsequent calls,
- Callback passed to
times
will beresolverFn
ifn === 1
. Make sure to bindresult
before you pass or do() => resolverFn(result)
- Else pass
fn
- Callback passed to
- Store output of this call, say
innerPromise
and on it's then, callresolverFn
withresult