【问题标题】:async await with multiple setIntervals具有多个 setIntervals 的异步等待
【发布时间】:2019-06-11 22:44:49
【问题描述】:

我有两个 API 请求,一个每 5000 毫秒调用一次,一个每 30000 毫秒调用一次。我想确保在向服务器发出新请求之前完成每个调用。我不希望任何一个请求相互重叠。例如,如果 func1 API 尚未完成,那么我不想在完成之前发出 func2 API 调用。

这是我迄今为止尝试过的。

async function vals() {
            try {
                await func1()
                    .then(response => response.json())
                    .then(result => display_result(result))
                    .catch(error => console.error('Error: ', error));
                await func2()
                    .then(response => response.json())
                    .then(result => display_result(result))
                    .catch(error => console.error('Error: ', error));
            }
            catch(error) {
                return error;
            }
}

vals();

这里是func1和func2。

function func1() {
        return setInterval(() => fetch(url, { method: 'GET' }), 5000);
}

function func2() {
        return setInterval(() => fetch(url, { method: 'GET' }), 30000);
}

我希望它首先运行 func1(),等待它解决,然后运行 ​​func2()。相反,func1() 被调用了两次,并且永远不会到达 func2()。是否应该在 vals() 函数中设置 setIntervals?任何指导来完成这项工作将不胜感激。

【问题讨论】:

  • 如何使用Promise.all,当所有作为迭代传递的承诺都已解决时,它会解决。
  • 如果您的 API 可以容忍两个请求同时发生 不频繁 发生,那么有一个非常简单的解决方案适合您:确保超时延迟没有小的共同点-多! 5000ms30000ms 循环将在每个 30000ms 中重合,但 5000ms29000ms 循环重合的频率要低得多。更好的是,将任一循环的初始化偏移2500ms - 这样循环将永远不会在时间上重合。
  • 我更新了我的问题。我需要来自 func1 或 func2 的每个 API 调用按顺序运行。 Promise.all 并行运行所有的 Promise。
  • 您通常不会在 async 函数中使用then。您应该简单地等待结果值并使用它们。还有setIntervaldoesn't return a promise,所以等待结果会立即解决。
  • 您考虑过使用自定义事件吗?使用事件的原因在于它们的工作方式。如果在触发具有相同签名的另一个事件时正在执行事件处理程序,则第一个事件将始终在下一个事件回调执行之前执行到完成。这是一种确保一个任务在第一个任务完成之前不会启动的简单方法(如果任务、事件处理程序相同)。

标签: javascript async-await


【解决方案1】:

好的,这有点棘手!您有两个不同的时间间隔生成任务(http 请求),这需要大量时间,并且您希望确保这些任务不会彼此重合。

我建议不要在超时后立即激活您的请求,而是将请求添加到待完成的工作队列中。该队列将尽快处理一系列串行任务。

// In your example your "long-running-tasks" are http requests.
// In this example I'll use a timeout.
let genLongRunningTask1 = async () => {
  console.log('Task 1 start');
  await new Promise(r => setTimeout(r, 1500));
  console.log('Task 1 end');
};

let genLongRunningTask2 = async () => {
  console.log('Task 2 start');
  await new Promise(r => setTimeout(r, 1600));
  console.log('Task 2 end');
};

// The tail of the promise-queue. If it resolves we're ready
// to begin a new long-running-task. It's initially resolved.
let queueTail = Promise.resolve();
let queueNewTask = async genLongRunningTask => {
  await queueTail;
  await genLongRunningTask();
};

// Now setup our intervals. We don't directly generate any
// long-running-tasks here - instead we "queue" them, and
// then point the tail of the queue to their completion.
console.log('Starting...');
setInterval(() => {
  queueTail = queueNewTask(genLongRunningTask1);
}, 3000);
setInterval(() => {
  queueTail = queueNewTask(genLongRunningTask2);
}, 6000);

在我的示例中,两个间隔分别位于 3000ms6000ms,因此它们应该同时运行每个 6000ms - 但您会看到排队逻辑使它们保持良好和独立!在上一个任务结束之前,您永远不会看到新任务开始。

在您的情况下,您应该只需要编辑 genLongRunningTask1genLongRunningTask2 以便它们等待并处理您的请求。类似于以下内容:

let genLongRunningTask1 = async () => {
  try {
    // Assuming `func1` returns a "response object":
    let response = await func1();
    
    /*
    Assuming the "response object" has a `json` method,
    and `display_result` is an async method for showing
    the json data.
    NOTE: this use of `await` ensures requests will remain queued
    until the previous request is done processing *and* rendering.
    To begin sending the next request after the previous request
    has returned, but overlapping with the period in which that
    request is still *rendering*, omit `async` here.
    */
    await display_result(response.json());
  } catch(err) {
    console.error('Error:', err);
  }
};

警告:请注意,您排队任务的速度不要超过任务完成的速度!

【讨论】:

  • 哎呀,如果其中一项任务花费的时间比它们的间隔时间长,你会得到一个讨厌的错误。 queueTail = ... 在分配之前不会等待上一个任务。
  • 你错了,你可以自己试一试——即使genLongRunningTask1 需要4000ms 才能完成(尽管队列会随着时间的推移无限增长),它也确实有效。以这种方式重新分配queueTail 不会破坏队列中已经存在的任何任务。
  • 确实你是对的。看起来queueNewTask 在新分配给queueTail = ... 之前等待queueTail
  • @GershomMaes 感谢您的帮助。 Task1 需要约 2 秒才能完成,而 Task2 需要约 7 秒。在这种情况下,我仍然看到一个新的 Task2 在最后一个 Task1 完成之前开始。有没有办法防止这种情况发生?
  • 您可能将其与您自己的代码错误地组合在一起!请参阅我的编辑,其中显示了如何使用自己的代码进行此操作的示例。
猜你喜欢
  • 1970-01-01
  • 2021-10-14
  • 2015-05-22
  • 1970-01-01
  • 2018-12-31
  • 2021-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多