【问题标题】:Promises yielded from a generator not resolved concurrently when awaiting生成器产生的承诺在等待时未同时解决
【发布时间】:2019-07-17 18:28:36
【问题描述】:

我正在使用生成器函数来模拟可迭代的任务队列:

function* taskQueue() {
    for(let i=0; i<10; i++) {
        yield new Promise((resolve) => {
            setTimeout(() => resolve(i), (11 - i)*1000)
        })
    }
}

超时应该导致 promise 以相反的顺序解决(假设所有都同时开始),最长在 11 秒内完成。

遍历promise,并使用Promise.then(),代码获取长度 最长的超时时间(11 秒):

function then() {
    let q = taskQueue();
    for(i of q) {
        i.then(console.log)
    }
}

但是,如果我等待承诺,那么它们会按顺序解决,并且需要线性时间(66 秒)才能完成:

async function awaiting() {
    let q = taskQueue();
    for(i of q) {
        let n = await i;
        console.log(n);
    }
}

这似乎是因为在前一个承诺解决之前 taskQueue 没有产生下一个承诺,因为如果我将它们推送到一个数组然后等待,代码将在 11 秒内执行:

async function awaiting2() {
    let q = taskQueue();
    let tasks = [];
    for(i of q) {
        tasks.push(i);
    }
    for(i of tasks) {
        let n = await i;
        console.log(n);
    }
}

那么为什么在 then() 的情况下 Promise 是同时解决的,而在 awaiting() 的情况下却不是呢?

【问题讨论】:

  • “超时应该导致承诺以相反的顺序解决” 不确定您所说的“相反顺序”是什么意思?预期的结果是什么?
  • 超时设置为 11 - i 秒。因此,第一个承诺的超时时间为 11 - 0 = 11 秒,第二个为 10 秒,依此类推,直到第 10 个超时时间为 1 秒。
  • 仍然不确定预期的结果是什么?对于第一个示例处代码处的整个程序,总共需要 11 秒才能完成?还是让程序将11 + 10 + 9 + N-1 的总和通过到0 来完成?你想解决什么问题?
  • 关键是要创建一组承诺,这些承诺都需要不同的时间才能完成。如果代码以并发方式执行,那么我希望总执行时间接近最长超时时间,就像我使用 Promise.then() 的情况一样。奇怪的是,当等待 promise 时,执行时间反而更长。
  • 第一个示例中的代码确实需要 11 秒才能完成。使用async/await 执行awaiting 处的代码按顺序执行。这也是预期的结果。使用的模式是一种选择,取决于您要实现的目标。

标签: javascript node.js promise async-await generator


【解决方案1】:

可以调整第一个示例中的代码以输出与使用 asycn/await 的代码在使用 recursion 的模式中使用 .next().then() 的结果相同的结果“一个碰巧引用自身的非终止过程。” What are the boundaries of recursion?

function* taskQueue() {
  for (let i = 0; i < 10; i++) {
    yield new Promise((resolve) => {
      setTimeout(() => resolve(i), (11 - i) * 1000)
    })
  }
}

const queue = taskQueue();

const awaiting = ({value:promise, done}, q) => 
  done ? {done} : promise.then(data => (console.log(data), awaiting(q.next(), q)));

awaiting(queue.next(), queue)
.then(console.log)
.catch(console.error)

【讨论】:

    【解决方案2】:

    这是因为Promises 自然是异步的,因为无论 Promise 处于何种状态(Pending、Resolved、Error),程序的其余部分都会继续执行。

    通过使用 async 关键字,您告诉功能块等待使用 await 关键字指定的每个 Promise 等待该 Promise 解决后再继续执行,这使得该功能块同步。

    通过在 Promise 上调用 .then() 方法,您将让 Promise 异步执行,但无论您的程序中发生什么其他情况,都会在它完成时调用 .then()

    【讨论】:

    • 有趣!我读过的关于async/await 的文章暗示该行为与调用.then() 相同,但从您的回答看来,如果我想异步处理承诺,那么我应该使用.then() 而不是@ 987654331@。谢谢。
    • @wolfson109,你也可以这样做:Array.from(taskQueue()).forEach(async (i) =&gt; { let n = await i; console.log(n); })。尽管使用了async/await,但这段代码也是异步的。
    • 是的,async/await 旨在更优雅地处理 Promise 的同步性。您可能正在寻找的是Promise.all(),它可以让您await(或致电.then())解决一系列Promise。
    猜你喜欢
    • 1970-01-01
    • 2021-12-30
    • 1970-01-01
    • 2019-04-29
    • 2018-04-02
    • 2021-11-26
    • 2014-10-29
    • 2018-03-19
    • 1970-01-01
    相关资源
    最近更新 更多