【问题标题】:Run Promise all in an await for loop在 await for 循环中运行 Promise all
【发布时间】:2021-10-30 00:47:10
【问题描述】:

我有一个 Promise 函数的嵌套数组。例如:

let callSet = [
 [ func1 , func2 , func3],
 [ func4 , func5 , func6],
 [ func7 , func8 , func9],
]

await func1() 的响应将在以下结构中:

{
 data : [ {id: 1} , {id:2}],
 status: 400
}

我想在 for 循环中运行它,以便它们按顺序批量运行,并在数据进入时将数据增量加载到数组中。尝试了下面的代码,但我不知道该怎么做:

  const finalData = [];
  private asyncForEach(PromiseGroups): Promise<any> {
    return PromiseGroups.reduce(async (acc, cItem) => {
      const results = await acc;
      const res = await Promise.all(cItem) as any;
      finalData  = [...finalData  , ...[].concat(...res.map(r => r.data))];
      return results
    }, Promise.resolve([]))
  }

我想将其加载为:

[ {id: 1}, {id:2} , {id: 3} ..... ]   

随着 Promise 的解决,这应该会得到更新

我想等到 func1 , func2 , func3 解决后再转到 func4 , func5 , func6 。一旦我得到func4 , func5 , func6的数据,我想用func1 , func2 , func3的数据推送它

【问题讨论】:

  • 在 reducer 回调中等待不会导致 reduce 函数为 await 回调
  • 您能否详细说明按顺序和增量批量运行。我怀疑你的功能比 ikhvjss 答案中的要多。
  • @Newbie:是的,你是对的,我想等到func1 , func2 , func3 解决后再转到func4 , func5 , func6。一旦我得到`func4 , func5 , func6`` , I want to push it with the data of func1,func2,func3`的数据`
  • @JuanMendes:请您指导正确的方法
  • 最简单的方法?不要使用reduce,只需使用for of 填充一系列承诺。见stackoverflow.com/questions/41243468/…

标签: javascript typescript async-await promise


【解决方案1】:

试试下面的代码:


  private init(){
   let callSet = [
     [ func1 , func2 , func3],
     [ func4 , func5 , func6],
     [ func7 , func8 , func9],
   ];
   this.asyncForEach(callSet,this.fetchInBatch.bind(this) )
  }

  private asyncForEach(funcGrpList, execFunc) {
    return funcGrpList.reduce((p,funcList) => {
        return p.then(() => execFunc(funcList));
    }, Promise.resolve());
  }

  private fetchInBatch(pageGroupList) {
    return Promise.all(pageGroupList).then((res: any) => {
      this.finalData = [...this.finalData  , ...[].concat(...res.map(r => r.data))];
    }) 
  }

这应该可以正常工作

【讨论】:

    【解决方案2】:

    编辑:假设最后一个数组结果直接返回,因为不需要等待下一个循环完成。

    async function run(callSet) {
      const output = [];
      let prev = [];
      const len = callSet.length;
    
      for (let i = 0; i < len; i++) {
        const array = await Promise.all(callSet[i].map(func => func()));
        const data = array.map(item => item.data);
    
        if (i === 0) {
          // no need to append item to output
          // just append item to previous array for next loop to use.
          prev.push(...data);
        } else if (i < len) {
          // append item to output from previous result.
          output.push(...prev);
          prev = [];
          // append data to previous result for next loop.
          prev.push(...data);
        } else {
          //last loop, just append data from previous result and current result
          output.push(...prev);
          output.push(...data);
        }
      }
      console.log(output);
    }
    

    【讨论】:

    • @Newbie:是的,你是对的,我想等到func1 , func2 , func3 解决后再转到func4 , func5 , func6。一旦我得到`func4 , func5 , func6`` , I want to push it with the data of func1,func2,func3`的数据`
    • @ikhvjs 我不明白reduce 的这种用法如何与async 一起使用。通过渐进式包装,Reduce 可以与async 一起使用,但它很容易出现拼写错误,因此应避免使用。
    • @Newbie,我更新了我的答案。一开始我误解了OP的要求。
    • 我已经看到这个答案在 1 小时内更改了 5 次,并且代码完全不同并且每次都变得更加复杂。请停止尝试通过反复试验来回答。
    • @Newbie,现在应该是正确的。我更改了代码,因为 OP 更改了他的要求。
    【解决方案3】:

    这将按照请求的顺序和时间调用执行集,并在承诺组返回时添加返回数据

    const finalData = [];
    
    async function execute() {
      for (const set of callSet) {
        const resp = await Promise.all(set.map(f => f()));
        finalData.push(...resp.map(r => r.data).flat());
      }
    }
    

    一旦execute() 被调用,finalData 将针对每个“行”函数异步更新一次。

    给未来的读者

    resp.map(r =&gt; r.data).flat() 是由于指定的承诺有效负载。如果有人只需要将结果打包在一起,代码将是:

    for (const set of callSet) {
      const resp = await Promise.all(set);
      finalData.push(...resp);
    }
    

    【讨论】:

    • OP:我想等到 func1 , func2 , func3 解决后再转到 func4 , func5 , func6 。一旦我得到func4 , func5 , func6 的数据,我想用func1 , func2 , func3 的数据推送它。我想你误会了。
    【解决方案4】:

    如果您想以块的形式加载数据但生成扁平的结果数组,您最简单的选择是使用async/await 语法:

    interface Data {
      id: number
    }
    interface DataResponse {
      data: Data[];
      status: number;
    }
    type AsyncCall = () => Promise<DataResponse>;
    
    /* ... */
    
    const result: Data[] = [];
    for(const chunk of callSet) {
      const chunkResult = await Promise.all(chunk.map(f => f()));
      result.push(...chunkResult.flatMap(x => x.data));
    }
    

    Playground Link

    JavaScript 演示:

    /* mock section */
    const fakeFunc = (id1, id2) => ()=>
      Promise.resolve({
        data : [{id: id1} , {id: id2}],
        status: 400
      });
    
    const func1 = fakeFunc(1, 2),
          func2 = fakeFunc(3, 4),
          func3 = fakeFunc(5, 6),
          func4 = fakeFunc(7, 8),
          func5 = fakeFunc(9, 10),
          func6 = fakeFunc(11, 12),
          func7 = fakeFunc(13, 14),
          func8 = fakeFunc(15, 16),
          func9 = fakeFunc(17, 18)
          ;
          
    /* /mock section */
    
    async function main() {
      let callSet = [
        [ func1 , func2 , func3],
        [ func4 , func5 , func6],
        [ func7 , func8 , func9],
      ];
    
      const result = [];
      for(const chunk of callSet) {
        const chunkResult = await Promise.all(chunk.map(f => f()));
        result.push(...chunkResult.flatMap(x => x.data));
      }
      
      return result;
    }
    
    main()
      .then(r => console.log(r));

    我你更喜欢只使用 Promise API,而不是 async/await,那么你可以缩减成这样的 Promise:

    interface Data {
      id: number
    }
    interface DataResponse {
      data: Data[];
      status: number;
    }
    type AsyncCall = () => Promise<DataResponse>;
    
    /* ... */
    
    const result = callSet.reduce((p: Promise<Data[]>, chunk: AsyncCall[]) =>
       p.then(acc =>  
          Promise.all(chunk.map(f => f()))
            .then(chunkResult => acc.concat(chunkResult.flatMap(x => x.data))))
      , Promise.resolve([]));
    

    Playground Link

    JavaScript 演示:

    /* mock section */
    const fakeFunc = (id1, id2) => () =>
      Promise.resolve({
        data : [{id: id1} , {id: id2}],
        status: 400
      });
    
    const func1 = fakeFunc(1, 2),
          func2 = fakeFunc(3, 4),
          func3 = fakeFunc(5, 6),
          func4 = fakeFunc(7, 8),
          func5 = fakeFunc(9, 10),
          func6 = fakeFunc(11, 12),
          func7 = fakeFunc(13, 14),
          func8 = fakeFunc(15, 16),
          func9 = fakeFunc(17, 18)
          ;
          
    /* /mock section */
    
    function main() {
      let callSet = [
        [ func1 , func2 , func3],
        [ func4 , func5 , func6],
        [ func7 , func8 , func9],
      ];
    
      const result = callSet.reduce((p, chunk) =>
        p.then(acc =>  
          Promise.all(chunk.map(f => f()))
            .then(chunkResult => acc.concat(chunkResult.flatMap(x => x.data))))
        , Promise.resolve([]));
    
      return result;
    }
    
    main()
      .then(r => console.log(r));

    但是,它有点难看。可以通过提取一些函数来改进:

    interface Data {
      id: number
    }
    interface DataResponse {
      data: Data[];
      status: number;
    }
    type AsyncCall = () => Promise<DataResponse>;
    
    /* ... */
    
    const combineWith = (acc: Data[]) => (chunkResult: DataResponse[]) =>
      acc.concat(chunkResult.flatMap(x => x.data));
    
    const process = (chunk: AsyncCall[]) => (acc: Data[]) =>  
          Promise.all(chunk.map(f => f()))
            .then(combineWith(acc));
    
    /* ... */
    
    const result = callSet.reduce((p: Promise<Data[]>, chunk: AsyncCall[]) =>
        p.then(process(chunk))
      , Promise.resolve([]))
    

    Playground Link

    JavaScript 演示:

    /* mock section */
    const fakeFunc = (id1, id2) => () =>
      Promise.resolve({
        data : [{id: id1} , {id: id2}],
        status: 400
      });
    
    const func1 = fakeFunc(1, 2),
          func2 = fakeFunc(3, 4),
          func3 = fakeFunc(5, 6),
          func4 = fakeFunc(7, 8),
          func5 = fakeFunc(9, 10),
          func6 = fakeFunc(11, 12),
          func7 = fakeFunc(13, 14),
          func8 = fakeFunc(15, 16),
          func9 = fakeFunc(17, 18)
          ;
          
    /* /mock section */
    
    const combineWith = (acc) => (chunkResult) =>
      acc.concat(chunkResult.flatMap(x => x.data));
    
    const process = (chunk) => (acc) =>  
          Promise.all(chunk.map(f => f()))
            .then(combineWith(acc));
    
    function main() {
      let callSet = [
        [ func1 , func2 , func3],
        [ func4 , func5 , func6],
        [ func7 , func8 , func9],
      ];
    
      const result = callSet.reduce((p, chunk) =>
        p.then(process(chunk))
      , Promise.resolve([]));
    
      return result;
    }
    
    main()
      .then(r => console.log(r));

    【讨论】:

    • 非常感谢,我会检查一下并告诉您。同时,我在stackoverflow.com/questions/69000182/… 中实现了另一种方法。但我遇到了另一个问题。你能看一下吗。那真的很有帮助。
    猜你喜欢
    • 2022-10-16
    • 1970-01-01
    • 2019-01-18
    • 1970-01-01
    • 2018-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多