【问题标题】:Promise nested inside async function - why not working only with async/await?Promise 嵌套在 async 函数中 - 为什么不能只使用 async/await?
【发布时间】:2021-01-26 09:47:43
【问题描述】:

TL;DR

无法让async/await 函数完成async 函数通过返回自定义new Promise 对象实现的功能。


我正在尝试构建一个函数,该函数接受一个字符串,遍历字符串中的单词,并设置一个间隔,在您分配的设置间隔之后记录每个单词。完成记录后,回调将在函数完成记录每个单词后记录单词的总数。下面,你有 main 函数。

async function textReader (text, callback, interval = 1000) {
  return new Promise((resolve, reject) => {
    let counter = 0
    let textArray = text.trim().split(' ')
    let idInterval = setInterval( () => {    

      if(counter == textArray.length) {
        callback(textArray.length)
        clearInterval(idInterval)
        resolve();

      } else {
        console.log(textArray[counter++])
      }
    }, interval)
  })
}

然后,记录显示字数的回调:

function funCallback (wordQuantity) {
  console.log(`Process complete - The text contains ${wordQuantity} words`)
}

最后,一个测试主函数的async 函数。它只是简单地运行了 3 次 main 函数,并按照应有的方式一个接一个地记录它们中的每一个。这个想法是每个await 阻塞进程,直到有一个解析值(这实际上意味着:当它在终端中记录每个单词时),一旦await 完成,跳转到下一个textReader 函数等等。

async function test () {
  try {
    let time = 500
    await textReader('When I find myself in times of trouble ', funCallback, time)
    await textReader('mother Mary comes to me ', funCallback, time)
    await textReader('speaking words of wisdom Let it be', funCallback, time)
  } catch (err) { console.log(err)}
}

我的问题是我希望textReader 函数能够实现相同的行为,而不必返回new Promise,而是使用await(因为我猜这就是async/await 函数应该有帮助,对吧?实现与 ES6 相同Promise)

请记住,整个计划的目标是:

  • 记录特定区间内的单词
  • 如果test() 拥有多个textReader(),则它们必须处于阻塞状态,这意味着一个必须等​​待另一个完成记录其单词,否则来自所有测试函数的单词将相互重叠- 顺便说一句,这会让人很困惑 -.
  • 计算每个字符串中记录了多少字。

我只是不明白如何在不从textReader()new Promise 返回但使用await 的情况下解决它,因为它应该在async/await 函数中。

async/await(见下文)解决它的尝试之一没有奏效;它只是同时运行来自test() 的3 个textReader() 函数,重叠日志。

async function textReader (text, callback, time = 1000) {
  let counter = 0
  let textArray = text.trim().split(' ')
     
  let loggingWords = async () => {
        let idInterval = setInterval( () => {    
    
          if(counter == textArray.length) {
            callback(textArray.length) 
            clearInterval(idInterval)     
          } else { 
            console.log(textoArray[contador++]) 
          } 
        }, time) 
      }

      let waitForLogingWords = await loggingWords()
      
      return waitForLogingWords
    };

【问题讨论】:

  • 您不能将setInterval 与promise 一起使用。在 awaited delay 函数周围放置一个循环(对于 setTimeout - you can't completely avoid new Promise there 返回 new Promise)。
  • “因为我猜这就是 async/await 函数应该有帮助的,对吧?实现与 ES6 Promise 相同的功能” - 否。async/@987654354 @ 隐藏承诺。它使它看起来没有任何承诺。它不会让承诺消失。 async/await 所做的一切都是由幕后的承诺提供支持的。 async/await 承诺。避免使用new Promise() 本身并不会使您的代码更好,使用它本身不会使代码变得更糟。
  • 您应该避免的是 callback 参数。这就是为什么 Promises 首先存在的全部意义 - 将执行任务的函数与任务使用者分离。将消费者的回调传递给工作人员打破了这种范式。

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


【解决方案1】:

如 cmets 所示,您无法完全避免调用 new Promise()。一旦确定了这一点,我们不妨接受它。

这里是“我想使用promises来处理一个按顺序处理一个项目列表,每个项目之间有一个延迟”问题的通用解决方案。

const sequence = (iterable, payload, delay=0) =>
  iterable.reduce((p, item) =>
    p.then(() =>
      new Promise(r => setTimeout(r, delay)).then(() => payload(item))
    ), Promise.resolve()
  );

它需要一个项目列表并创建一个承诺链,每个承诺在前一个承诺之后履行delay 毫秒。

基于此,很容易实现您的textReader - 这是一个sequence over words 记录每个单词:

var textReader = (text, interval=1000) =>
  sequence(text.trim().split(' '), word => console.log(word), interval);

您的再次处理歌曲的函数只是一个sequence,所以它同样容易实现。这一次作为 async 函数(但这只是细节 - sequence(...).then(...) 会起作用):

async function test() {
  try {
    await sequence([
      'When I find myself in times of trouble ',
      'mother Mary comes to me ',
      'speaking words of wisdom Let it be'
    ], line => textReader(line, 500));
    console.log('-all done-');
  } catch (err) {
    console.log(err);
  }
}

一旦我们运行test(),我们就会得到一个整齐交错的单词输出,每 1/2 秒一个接一个:

什么时候 我 寻找 我 在 次 的 麻烦 母亲 玛丽 来了 至 我 请讲 字 的 智慧 让 它 是 -全部完成-

【讨论】:

  • 您仍然应该避免sequence 中的Promise constructor antipattern,因为您不是p 中的错误,也不是payload 中的异常。将它放在一个单独的 delay 函数中,然后在 sequence 中使用正确的承诺链(或 async/await)。
  • @Bergi 上面的承诺只有链超时,除非我错过了什么,否则它们不会出错。在payload 中捕获异常是调用者的责任。
  • 如果payload() 返回一个被拒绝的承诺,他们可能会出错。不,捕获异常是 Promise 链的责任,处理被拒绝的 Promise 是调用者的责任。
  • 嗯...如果有其他延迟调用resolve() 的方式,我会避免使用new Promise()。除了创建一个新的 Promise 之外,我不知道有任何方法可以做到这一点其他,因为只有 Promise 构造函数可以访问 resolve() 回调。
  • 我并不是说你可以完全避免new Promise,我是说你不应该在执行器内部使用承诺。正确的解决方案是 array.reduce((p, item) => p.then(() => delay(t)).then(() => callback(item)), Promise.resolve())delay = t => new Promise(res => { setTimeout(res,t); });
猜你喜欢
  • 2023-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-21
  • 2020-09-22
相关资源
最近更新 更多