【问题标题】:Promise.all Replacement?Promise.all 替换?
【发布时间】:2020-02-07 11:54:47
【问题描述】:

有没有什么方法可以让我在 Javascript 中调用多个异步函数,但在一个完成后立即获得调用响应,这与等待所有异步调用完成的 Promise.all 不同? 我想要的是,并行运行异步调用,并在其他调用仍在运行时立即获得相应调用的响应,然后更新反应状态。

我已经将 Promise.all 用于多个异步调用,但它会在所有调用完成后给出响应。

【问题讨论】:

  • 还需要Promise.all()的行为,还是不需要?
  • 我想你正在寻找Promise.race。要更好地理解 Promise.race,请参阅 this
  • 你所描述的只是在你所有的 Promise 中添加相同的回调,这真的正是你想要的吗?
  • 我想调用异步函数,我需要每个函数的响应。但是使用 promise.all,当所有调用都成功完成时,我会得到所有响应。我想要的是,调用是异步进行的,一旦每个调用完成,我就会不断得到响应

标签: javascript asynchronous parallel-processing


【解决方案1】:

您可以迭代一系列承诺并更改每个承诺解决方案的状态:

let state = null;

const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve({p1: 1});
  }, 2000);
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve({p2: 2});
  }, 1000);
});
const p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve({p3: 3});
  }, 3000);
});

// Iterate promises
[p1, p2, p3].forEach(p => {
  p.then(res => {
    state = Object.assign({}, state, res);
    console.log(state);
  });
});

console.log(state);

您可以为此任务创建自己的Promise 方法:

if (!Promise.each) {
  Promise.each = function(promises, callback, error) {
    if (promises && Array.isArray(promises)) {
      promises.forEach(p => {
        p.then(res => {
          callback(res);
        }, err => {
          if (error) {
              error(err);
          } else {
              console.log(err);
          }          
        });
      });
    }
  }
}

// Usage
Promise.each([p1, p2, p3], updateState);

【讨论】:

  • 老实说,我会选择Promise.each = function each (iterable, onFulfilled, onRejected) { for (const value of iterable) this.resolve(value).then(onFulfilled, onRejected); };
  • 我的 2 美分:失去对新 Promise 的控制是 IMM 次优的。您可能更愿意至少返回映射输入的结果,而不是无菌的 forEach。这样就可以做到 Promise.all( Promise.each( promises, doSomethingForEach ) ).then( doSomethingWhenAllDone )。
【解决方案2】:

在您的问题中,“比更新...状态”是关键。您计划以这样一种方式更新状态,即一旦一个承诺“完成”,相应的状态就会更新。你一直在尝试这样的事情

async function f1(){ await Promise.resolve(); }
async funciton f2(){ await Promise.resolve(); }
async function fn(){ await Promise.resolve(); }

async function drvAsync(){
  const [r1, r2, rn] = await Promise.all([f1(), f2(), fn()]);
  u1(r1);
  u2(r2);
  un(rn);
}

其中 f[n] 是一个异步业务函数,u[n] 是一种处理其结果的方法。此架构在您的场景中是不可接受的。也许 fn 完成得比其他人更快,并且您想更早地更新第 N 个状态。

我的建议是完全不使用同步原语。相反,您应该单独处理结果。

function drv(){
  f1().then((r1)=>u1(r1)).catch((e)=>er(e));
  f2().then((r2)=>u2(r2)).catch((e)=>er(e));
  fn().then((rn)=>un(rn)).catch((e)=>er(e));;  
}

此架构将调用每个更新 (u[n]) 方法,而无需等待其他方法的结果。

【讨论】:

  • 你说得对!在我的场景中,我有一个项目列表,其长度未知。在我目前的情况下如何使用它? items.forEach(item => { apiCall(item).then(.....) }) 会并行工作吗?
  • 是的,它会的。第一阶段 apiCall 将开始为每个项目运行。在第二阶段,than / catch 中的回调将被适当地调用。
  • 还有一件事@mx.Wolf,我尝试了你的实现,它就像一个魅力。现在,我怎么能等待所有人完成,无论其中任何一个是否错误?
  • 哦,那是另一个故事了。这次你确实使用了 Promise.all,但将每个 apiCall 包装在 async ()=>{ try{await apiCall(item)} catch(e){log (e)}} 或类似的构造中,以显示没有拒绝 Promise 的迹象。全部
  • 我通过将每个 api 调用包装在 promise 中来处理它。首先,在“then”中更新状态,然后解决或拒绝承诺。并在数组中添加所有调用并使用 promise.all 等待所有调用。状态,分别在“then”中更新,同时我们也在 promise.all 中等待所有调用完成。谢谢!
【解决方案3】:

只要您不使用 async/await,您就可以并行执行调用。请注意,浏览器对网络请求有连接限制,因此如果您执行例如 100 个调用,它将不是并行的,而是排队的。

当承诺解决时,您需要一个回调来执行。如果所有函数的回调都相同,而不是循环所有调用,则创建承诺并使用相同的函数解析。

function resolved(data) {
    // ...
}

const promise1 = new Promise(function(resolve, reject) {
    resolve('Success!');
}).then(resolved);

const promise2 = new Promise(function(resolve, reject) {
    resolve('Success!');
}).then(resolved);

【讨论】:

    【解决方案4】:

    这就是Promise.race 的用途:

    Promise.race() 方法返回一个承诺,一旦可迭代中的一个承诺履行或拒绝,该承诺就会履行或拒绝,并带有来自该承诺的价值或原因。

    例如:

    function sleep(ms) {
      return new Promise((resolve) => setTimeout(resolve, ms));
    }
    
    async function main() {
      const promises = [
        sleep(1000).then(() => "world"),
        sleep(100).then(() => "hello"),
      ];
      const promisesWithIndices = promises.map((p, index) =>
        p.then((result) => ({result, index}))
      );
      while (promisesWithIndices.length > 0) {
        const {result, index} = await Promise.race(promisesWithIndices);
        console.log("%s -> %s", index, result);
        promisesWithIndices.splice(index, 1);
      }
    }
    
    main();  // prints "1 -> hello"; then prints "0 -> world" after a while
    

    【讨论】:

    • 不,仅 Promise.race 不是解决方案,您只会拥有 OP 愿意拥有的条件之一(从那里无法处理下一个 Promise 的解决)。
    • @Kaiido:怎么不呢?例如,您可以重复运行Promise.race,每次在较小的阵列上。 (添加了一个例子。)
    • 好吧,您现在远非 only Promise.race,您使用的迭代器可以通过将 async while 中的内容移动到一个简单的来直接使用回调你已经设置为你所有的承诺......
    猜你喜欢
    • 2018-09-07
    • 1970-01-01
    • 2021-03-19
    • 1970-01-01
    • 2020-12-19
    • 1970-01-01
    • 1970-01-01
    • 2016-10-01
    • 2019-12-23
    相关资源
    最近更新 更多