批量调用 $http.post 并链接承诺

Calling $http.post in batches and chaining promises

我有一个用例,我必须对输入数据的批次调用 $http.post(request)。 为此,我创建了一个请求数组。对于它们中的每一个,我需要获得 $http.post() 的响应,将其附加到现有数组并将其传递给渲染函数。我必须仅在前一个调用完成时才进行下一个调用,并且由于 $http.post() returns 一个承诺 (according to this),我正在尝试使用 reduce 函数来执行此操作。

function callToHttpPost(request) {
    return $http.post('someurl', request);
}

function outerFunc($scope, someId) {
    let completeArray = [];
    let arrayOfRequests = getRequestInBatches();

    arrayOfRequests.reduce((promiseChain, currentRequest) => {
        console.log(promiseChain);
        return promiseChain.then((previousResponse) => {
            completeArray.push.apply(completeArray, previousResponse.data);
            render($scope, completeArray, someId);
            return callToHttpPost(currentRequest);
        });
    }, Promise.resolve()).catch(e => errorHandler($scope, e, someId));
}

(我提到了MDN and this answer

但这给了我 TypeError: previousResponse is undefined。日志语句显示第一个承诺已解决(因为它是传递给 reduce 函数的初始值),但由于此错误,其他承诺显示为 rejected。我该如何解决?

传递初始值时出错。在reduce函数的第一次迭代中,Promise.resolve() returns undefined。这就是作为 previousResponse 传递的内容。将 Promise.resolve({ data: [] }) 作为 initialValue 传递给 reduce 函数解决了这个问题。

arrayOfRequests.reduce((promiseChain, currentRequest) => {
    console.log(promiseChain);
    return promiseChain.then((previousResponse) => {
        completeArray.push.apply(completeArray, previousResponse.data);
        render($scope, completeArray, someId);
        return callToHttpPost(currentRequest);
    });
}, Promise.resolve({ data: [] }))
.then(response => {
    completeArray.push.apply(completeArray, previousResponse.data);
    render($scope, completeArray, someId);
    displaySuccessNotification();
})
.catch(e => errorHandler($scope, e, someId));

(编辑以处理最终回复)

使用原版 Javascript

如果 outerFunc 函数可以在 async 上下文中使用(看起来可以,因为它 returns 什么都没有,结果传递给 render 在构建时发挥作用),您可以直接清理它,将其削减为:

async function outerFunc ($scope, someId) {
  const completeArray = []; 

  try {
    for (const request of getRequestInBatches()) {
      const { data } = await callToHttpPost(request);
      completeArray.push(...data);
      render($scope, completeArray, someId);
    }   
  } catch (e) {
    errorHandler($scope, e, someId);
  }
}

顺序性质将由 async/await 关键字强制执行。

使用 RxJS

如果您能够添加对 RxJS 的依赖,您可以将函数更改为:

import { from } from 'rxjs';
import { concatMap, scan } from 'rxjs/operators';

function outerFunc ($scope, someId) {
  from(getRequestInBatches()).pipe(
    concatMap(callToHttpPost),
    scan((completeArray, { data }) => completeArray.concat(...data), []) 
  ).subscribe(
    completeArray => render($scope, completeArray, someId),
    e => errorHandler($scope, e, someId)
  );  
}

围绕 Observable instead of Promise. In this version, the sequential nature is enforced by the concatMap operator, and the complete array of results is reduced and emitted while being built up by the scan 运算符的使用。