【问题标题】:Javascript: Run async task in series(or sequence) without librariesJavascript:在没有库的情况下连续(或顺序)运行异步任务
【发布时间】:2018-09-20 15:14:23
【问题描述】:

我想在一个循环中运行一些异步任务,但它应该按顺序执行(一个接一个)。它应该是 vanilla JS,而不是任何库。

var doSome = function(i) {
   return Promise.resolve(setTimeout(() => {
      console.log('done... ' + i)
   }, 1000 * (i%3)));
}

var looper = function() {
   var p = Promise.resolve();

   [1,2,3].forEach((n) => {
      p = p.then(() => doSome(n))
   })

   return p;
}

looper();

当前输出:

calling for ...1
calling for ...2
calling for ...3
Promise {<resolved>: 8260}
done... 3
done... 1
done... 2

预期输出:

calling for ...1
calling for ...2
calling for ...3
Promise {<resolved>: 8260}
done... 1
done... 2
done... 3

注意:请回答,如果您尝试过并且按预期工作

【问题讨论】:

  • 代码以 3、1、2 的顺序执行的唯一原因是您的模数数学错误。 1%3=1, 2%3=23%3=0,所以 1 等待 1000ms,2 等待 2000ms,但 3 等待 0ms。
  • Promise.resolve(setTimeout( wut... 你意识到它会立即用一个数字解决,因此不会等待超时,对吧?
  • 我没有看到calling for ... 在您的代码中记录的位置?
  • @KevinB - 问题中提出的代码并没有完全达到用户的意图,我不相信。我认为下面我的答案中的第一个 sn-p 更清楚地表示了用户试图描述的内容。

标签: javascript promise async-await es6-promise


【解决方案1】:

所以,从您下面的评论来看,我认为您自己的示例代码与您的描述不太匹配。我认为您想要的示例更接近以下 sn-p:

var doSome = function(i) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(`Completing ${i}`), 1000*(i%3))
  });
}

var looper = function() {
  [1,2,3].forEach((n) => {
    doSome(n).then(console.log);
  });
}

looper();

这里对数组[1, 2, 3]进行迭代,每一个生成一个异步过程。随着这些异步进程中的每一个完成,我们在它们上.then 并控制台记录它们的解析结果。

所以,现在的问题是如何最好地排队结果?下面,我将它们存储到一个数组中,然后利用async/await 来暂停对结果的执行,直到它们按顺序完成

// This is probably what you want
var doSome = function(i) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(`Completing ${i}`), 1000*(i%3))
  });
}

var looper = async function() {
  const nums = [1,2,3];
  const promises = []
  nums.forEach((n) => {
    console.log(`Queueing ${n}`);
    promises.push(doSome(n));
  });
  for (let promise of promises) {
    const result = await promise;
    console.log(result);
  }
}

looper();

现在,我们可以消除一个循环,只在最后一个完成后执行一个循环:

// Don't use this-- it is less efficient
var doSome = function(i) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(`Completing ${i}`), 1000*(i%3))
  });
}

var looper = async function() {
  const nums = [1,2,3];
  const promises = [];
  for (let n of nums) {
    console.log(`Queueing ${n}`);
    const result = await doSome(n);
    console.log(result);
  };
}

looper();

但是,正如您在日志中看到的那样,这种方法在前一个异步进程完成之前不会将下一个异步进程排队。这是不可取的,并且与您的用例不匹配。我们从之前的双循环方法中得到的是,所有异步进程都会立即执行,但随后我们对结果进行排序/排队,以便它们尊重我们预定义的顺序,而不是它们解析的顺序。

更新

关于Promise.allasync/await 和排队的预期行为:

所以,如果你想避免使用async/await,我认为你可以编写一些实用程序:

var doSome = function(i) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(`Completing ${i}`), 1000*(i%3))
  });
}

function handlePromiseQueue(queue) {
  let promise = queue.shift();
  promise.then((data) => {
    console.log(data)
    if (queue.length > 0) {
      handlePromiseQueue(queue);
    }
  })
}

var looper = function() {
  const nums = [1,2,3];
  const promises = []
  nums.forEach((n) => {
    console.log(`Queueing ${n}`);
    promises.push(doSome(n));
  });
  handlePromiseQueue(promises);
}

looper();

但是,让我澄清一下——如果用户 Bergi 的断言是正确的,那么尽快执行每个异步承诺并不重要当它解决时,只有在它们全部回来之前都不会对它们采取任何行动,那么这可以用Promise.all 100% 简化:

// This is probably what you want
var doSome = function(i) {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(`Completing ${i}`), 1000*(i%3))
  });
}

function handlePromiseQueue(queue) {
  let promise = queue.shift();
  promise.then((data) => {
    console.log(data)
    if (queue.length > 0) {
      handlePromiseQueue(queue);
    }
  })
}

var looper = function() {
  const nums = [1,2,3];
  const promises = []
  nums.forEach((n) => {
    console.log(`Queueing ${n}`);
    promises.push(doSome(n));
  });
  Promise.all(promises).then(() => handlePromiseQueue(promises));
}

looper();

最后,正如 Bergi 还指出的那样,我在这里玩得又快又松,没有在这些不同的承诺上设置任何 catch ——我在示例中为了简洁而省略了它们,但为了您的目的,您需要包含适当的处理错误或错误请求。

【讨论】:

  • 我这样做不是错误的,我故意想模仿最后一个任务与其他任务相比是短任务。即使任务很小,也应该按顺序运行。那是我的用例。不同执行时间的任务应该按照被调用的顺序运行。这是我的用例。您甚至不必更正这一点,只需举一个新示例即可。
  • @KamalakannanJ - 好的,让我确保我理解你的用例。您有三个将同时启动的异步进程,但是您希望确保它们的每个回调/.then 函数都以特定顺序执行,而不管异步进程解决的顺序如何?
  • 你是对的@Alexander。它将从一个 for 循环开始,并且应该以相同的顺序依次执行。
  • @Bergi - 问题是他不想在 all 承诺完成后做 one 的事情,他想触发所有异步一次,但按特定顺序排列每个回调并尽快触发每个回调。我看到了“如果失败了怎么办”的风险,但是打破循环的catch 可能会减轻这种风险,不是吗?
猜你喜欢
  • 2015-05-06
  • 1970-01-01
  • 2019-06-02
  • 2017-03-02
  • 2020-06-08
  • 2019-12-06
  • 2017-01-24
  • 1970-01-01
相关资源
最近更新 更多