处理递归时,除了使用 Promise 的 reduce 模式,还有什么替代方案?

4

注意:我不能使用 async

当我需要遍历一个数组并对其中的成员执行相同的函数并返回一个 Promise 时,我喜欢使用 reduce 模式,如下所示:

function get_count() {
  return new Promise(function(resolve, reject) {
    resolve(3);
  });
}

function recursively_execute(data) {
  return new Promise(function(resolve, reject) {
    resolve(data);
  });
}

function reduce_promise_pattern() {

  const get_batch_run_count = get_count();

  const batch_process = get_batch_run_count.then((count_value) => {

    const run_count = new Array(count_value).fill('batch');

    function recursive_function(data) {
      console.log('Running batch!');
      return recursively_execute(data).then(() => {
        return data;
      });
    }

    return run_count.reduce((previous_promise) => {
      return previous_promise.then((previous_response) => {
        test_data = {
          'test': 1
        };
        return recursive_function(test_data);
      })
    }, Promise.resolve())
  });
  return batch_process;
}

这将运行3次,因为run_count构建了一个包含3个元素的数组。虽然它可以工作,但对我来说感觉像是一种欺骗。
当我的列表已经预定义了唯一的项并且这些项被单独用作数据建立在那个reduce里面时,这种方法就可以工作,例如,如果我有3个步骤要完成,这3个步骤都是唯一的,每个步骤的数据都将在一个运行中使用...但在我的情况下呢?我只是让系统认为这些是不同的项目。
这方面的替代方法是什么?

情境对我来说并不是完全清晰的。你是从一个承诺数组还是一个普通值数组开始的?使用reduce的目的是什么?你想等到所有的承诺都被解决吗?因为在这种情况下,你也可以使用Promise.all()。对每个元素执行相同的函数可以使用几乎所有迭代方法来实现,而不仅仅是reduceforEachmap也可以工作,但选择取决于你实际想要实现什么。 - 3limin4t0r
进一步阐述上面的内容。我想代码片段中缺少一个清晰的例子。虽然你的代码片段没有问题,但它并没有澄清你想要实现什么。你能添加这样一个例子吗?例如,“我从第三方库获取了一组 Promise 数组。如果 Promise 解决了,我想为每个元素执行某个函数。” - 3limin4t0r
@3limin4t0r 忘记这个例子的具体“为什么”。我提出问题的原因是“这样做对吗?”如果你实际查看代码库,你会发现我在不必要地构建一个由相同单词填充的数组,其大小为N,其中N是我需要该迭代运行的次数。因此,我想知道是否有一种模式/解决方案可以使这个过程更好看。 - coolpasta
2个回答

3

你已经达到了 Promise 链的极限,尽管它们可以工作,但不易读懂。这就是为什么 async / await 被引入来处理这些情况,使用它们,您可以轻松地暂停各种(嵌套)循环,而不必为每个维护 Promise:

 async function reducePromisePattern() {
   for(let i = await getCount(); i >= 0; i--) {
     await recursiveFunction({'test': 1 });
   }
 }

如果您不能使用/转换 async,您仍然可以编写一些小助手来为您执行循环,例如:
 function loopAsync(times, fn) {
   function task() {
    times--;
    if(times <= 0) return;
    return fn().then(task);
   }

   return Promise.resolve().then(task);
 }

 function reducePromisePattern() {
   return getCount().then(function(count) {
      return asyncLoop(count, function() {
         return recursiveFunction({ test: 1 });
      });
   });
 }   

很遗憾,由于我的经验表明polyfills非常不可靠,我无法使用async。抱歉,如果我知道这可能是一个答案,我会在帖子中添加它的(现在我会添加)。 - coolpasta
你不能使用polyfill来实现语法,但可以通过转译来实现。Babel在这方面做得非常好,我从未遇到过任何问题。 - Jonas Wilms
请告诉我所需的软件包,如果它通过了我的测试,我会尝试使用await重写我的系统的某些部分。我完全不懂JS,所以请原谅我术语的错误使用。 - coolpasta

0

这里有两个不需要嵌套函数的选项。第一个选项简单地使用for循环,而第二个函数使用递归解决方案。两种解决方案的最后一个参数是可选的,只有在您想要将返回数据从一次运行传递到下一次运行时才应该使用它(类似于reduce)。

const sleep = () => new Promise(resolve => setTimeout(resolve, Math.random() * 1500 + 500));

// solution #1 - for-loop
function times1(n, callback, init) {
  var promise = Promise.resolve(init);
  
  for (; n > 0; --n) {
    promise = promise.then(val => callback(val));
  }
  
  return promise;
}

// example usage
times1(3, n => {
  console.log("solution #1 -", n);
  return sleep().then(() => n + 1);
}, 0);


// solution #2 - recursive
function times2(n, callback, init) {
  var promise = Promise.resolve(init);
  
  if (n <= 0) return promise;
  return promise.then(val => times2(n - 1, callback, callback(val)));
}

// example usage
times2(3, n => {
  console.log("solution #2 -", n);
  return sleep().then(() => n + 1);
}, 0);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接