【问题标题】:How to loop through/map an array to a function that returns a promise that includes another promise如何循环/映射数组到返回包含另一个承诺的承诺的函数
【发布时间】:2025-12-10 00:20:02
【问题描述】:

我一直在尝试循环遍历一组项目,以便每个项目都用于返回承诺的操作,但在该承诺中还有另一个承诺。我没有得到想要的流量。我究竟做错了什么?或者,还有更好的方法。由于条件,我不能使用太多的链接。我已经以多种方式尝试了 async/await 仍然是相同的不良结果。

为了清楚起见,代码已被简化。 fetch 调用实际上是数据库操作,但行为仍然相同;在这种情况下,我也使用了单元素数组。

var names = ['mike'];
console.log('black');

var fn = function doThings(name) {
  return new Promise((resolve) => {
    console.log('blue');

    var char = name.substr(1);
    getNum(char);
    console.log('violet');

    function getNum(ch) {
      console.log('green');

      fetch('fetchfrom.url')
      .then(response => {
        console.log('orange');

        return response.json();
      })
      .then(n => {

        if(n === 2) {
          console.log('red1');

          fetch('fetchfrom.url')
          .then(response => {
            console.log('yellow');

            return response.json();
          }).then(color => {
            if(n === 2) {
              console.log('red2');

              resolve(5);
            }
            else {
              console.log('brown2');

              resolve(10);
            }
          });
          console.log('lilac');

        } else {
          console.log('brown1');

          resolve(20);
        }

      });
    }

  })


}

var actions = names.map(fn);

Promise.all([actions])
.then(() => {
  console.log('done');
})

我希望日志按顺序排列(假设 n 始终等于 2): 黑色...蓝色...绿色...橙色...红色1...黄色...红色2...淡紫色...紫色...完成

但相反,我一直得到: 黑色...蓝色...绿色...紫色...完成...橙色...红色1...黄色...红色2...淡紫色

【问题讨论】:

    标签: javascript node.js es6-promise


    【解决方案1】:

    你需要正确地传播承诺。

    • 您的示例中不需要 new Promise() 构造函数,您需要在执行异步操作时正确调用 .then()
    • 当您在 .then() 处理程序中调用 fetch() 时,返回承诺以保持承诺链完整。
    • 异步操作后没有代码,例如:fetch().then();,因为之后的代码将立即执行,而不是在 fetch 调用完成后执行。

    您可以使用async / await,您的示例将如下所示:

    var names = ['mike'];
    console.log('black');
    
    async function getNum(ch) {
      console.log('green');
      let response = await fetch('fetchfrom.url');
      console.log('orange');
      let n = await response.json();
    
      if (n === 2) {
        console.log('red1');
        let res = await fetch('fetchfrom.url');
        console.log('yellow');
        let color = await res.json();
        if (n === 2) {
          console.log('red2');
          return 5;
        } else {
          console.log('brown2');
          return 10;
        }
        console.log('lilac');
      } else {
        console.log('brown1');
        return 20;
      }
    }
    
    async function doThings(name) {
      console.log('blue');
      var char = name.substr(1);
      let num = await getNum(char);
      console.log('violet');
      return num;
    }
    
    var actions = names.map(fn);
    Promise.all([actions])
      .then(() => {
        console.log('done');
      });
    

    没有async / await 和你可以做的只有简单的承诺:

    var names = ['mike'];
    console.log('black');
    
    function getNum(ch) {
      console.log('green');
      return fetch('fetchfrom.url').then(response => {
        return response.json();
      }).then(n => {
        console.log('orange');
        if (n === 2) {
          console.log('red1');
          return fetch('fetchfrom.url').then(res => {
            console.log('yellow');
            return res.json();
          }).then(color => {
            if (n === 2) {
              console.log('red2');
              return 5;
            } else {
              console.log('brown2');
              return 10;
            }
          }).then(result => {
            console.log('lilac');
            return result;
          });
        } else {
          console.log('brown1');
          return 20;
        }
      });
    }
    
    function doThings(name) {
      console.log('blue');
      var char = name.substr(1);
      return getNum(char).then(num => {
        console.log('violet');
        return num;
      });
    }
    
    var actions = names.map(fn);
    Promise.all([actions])
      .then(() => {
        console.log('done');
      });
    

    两者都应该产生预期的日志顺序。

    【讨论】:

    • 永远不要嵌套 then-ables,这是一个 promise 反模式。只需将它们返回到链中即可。
    • @robe007 如果你打破链条,这只是一种反模式,我没有这样做。但我同意这是一团糟——如果是我的代码,我更喜欢async / await,或者如果不可能将getNum重构为单个函数。我只是这样写的,所以 OP 可以将它与他的代码 sn-p 进行比较。
    • @robe007 不确定您在说什么 - 嵌套的 .then()s 不会影响错误处理。如果该路径中的任何地方出现任何故障,主链将出错并尝试查找下一个错误处理程序 .catch 或 .then(null, handler)。见jsfiddle.net/05bmhxoq。如前所述,如果可以避免的话,我不建议编写这种代码,但最好将其重新构造成单独的函数。
    【解决方案2】:

    plain promises 的另一种方法是使用 Promise.all 并玩 if-else 游戏,结果如下:

    // These Promises are for ilustrative purposes, but indeed are your fetch's
    const promiseA = Promise.resolve(2);
    const promiseB = Promise.resolve(2);
    
    // More than one 'name' in the array
    const names = ['mike', 'john'];
    console.log('black');
    
    const getNum = ch => {
      let response;
      console.log('green');
        return Promise.all([promiseA, promiseB]).then(([PromiseA, PromiseB]) => {
               //^^^^^^^^^^^^^^^^^^^^^^ Resolve all promises to use them now with if-else
        console.log('orange');
        if (PromiseA == 2) {
          console.log('red1');
          console.log('yellow');
          // In your example, you said: if(PromiseA==2). Here I use the second promise
          if (PromiseB == 2) {
            console.log('red2');
            response = 5;
          } else {
            console.log('brown2');
            response = 10;
          }
          console.log('lilac');
        } else {
          console.log('brown1');
          response = 20;
        }
        return response;
      });
    };
    
    const doThings = name => {
      console.log(`=>>>>> Evaluating name ${name}`);
      console.log('blue');
      const char = name.substr(1);
      return getNum(char).then(num => {
        console.log('violet');
        return num;
      });
    };
    
    // Immediately resolving promise, and then chain new promises
    // as the previous ones resolve
    let op = Promise.resolve();
    const actions = names.map(m => (op = op.then(() => doThings(m))));
    
    Promise.all(actions)
      .then(finalResponse => {
        console.log(`done ${finalResponse}`);
      })
      .catch(e => console.log(`There was an error ${e}`));

    此解决方案避免使用嵌套 then-ables,您的代码现在看起来更具可读性。

    注意:Promise.all 解决方案在语义上同时执行承诺。如果 semantical 执行是安全的,请查看here。但在所有情况下,无论是否语义执行,结果都是一样的。

    【讨论】: