【问题标题】:Recursive serial call promise递归串行调用承诺
【发布时间】:2021-11-21 23:58:38
【问题描述】:

我想实现对promise方法times的递归串行调用,将fn函数调用N次的结果返回到数组中。

目前我在times函数中增加了一个属性results,用来保存每次fn调用的结果。

我也不想使用模块范围的变量来保存结果。或者,通过传递其他参数(如times(fn, n, results))来保存结果,它会破坏函数签名。

async/await 语法是不允许的。

有没有办法只使用函数局部变量来保存结果?

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[] },
);

用法:

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' ]
}

【问题讨论】:

  • 你可以使用async/ await吗?
  • @Rajesh 更新了问题。这是不允许的。
  • 您正在尝试做一些承诺旨在解决的事情......顺序和回调地狱......也就是说,async/await 是最简单的选择。考虑递归但有点复杂

标签: javascript typescript promise


【解决方案1】:

根本没有理由使用有状态的result 变量。只需反转递归方向:

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;
    })
  });
}

但如果你坚持更渐进地创建 Promise,则需要使用累加器参数:

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);
  });
}

如果您不喜欢在您的签名中包含额外的可选参数,请使用执行递归的本地辅助函数:

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();
}

【讨论】:

    【解决方案2】:

    您想要实现的是具有顺序和递归模式。您可以按以下方式实现它

    Solution:

    • 在您的函数中创建 2 个承诺
      • finalPromise:从函数返回的承诺
      • promise:将通过 fn 创建的 Promise
    • 对于finalPromise,复制解析器函数的引用并将其存储起来以供手动调用,例如resolverFn
    • 创建一个变量result 来存储值。这应该是any[] 类型
    • promise.then,
      • 将收到的值推送到result
      • 随后调用时间。
    • 后续调用的技巧,
      • 如果n === 1,传递给times 的回调将是resolverFn。确保在通过之前绑定result 或执行() =&gt; resolverFn(result)
      • 否则通过fn
    • 存储此调用的输出,例如innerPromise,然后使用result 调用resolverFn

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-14
    • 2014-02-04
    • 1970-01-01
    • 2018-05-29
    • 1970-01-01
    • 1970-01-01
    • 2019-03-10
    • 2017-11-01
    相关资源
    最近更新 更多