【问题标题】:Calling promise while mapping over array映射数组时调用promise
【发布时间】:2019-12-16 14:05:38
【问题描述】:

我在地图中使用 Promise 时遇到了一些奇怪的行为。

这本身并不是一个真正的问题,但我想了解发生了什么。

let books = [
  {Name: "Moby Dick",
  AuthorId: 1
  },
  {Name: "The Great Gatsby",
  AuthorId: 2}
]

let authors = [
  {AuthorId: 1,
  Name: "Herman Melville"
  },
  {AuthorId: 2,
  Name: "F. Scott Fitzgerald"
  }
]

const getAuthorName = (AuthorId) => {
  return new Promise((resolve, reject) => {
     setTimeout(() => {
     resolve(authors.find((author) => {
        return author.AuthorId === AuthorId
        }).Name)
     }, 1000

  })
}
let responseArray = []
let promises = books.map((book) => (
  getAuthorName(book.AuthorId).then((res) => {
    responseArray.push({
      ...book,
      AuthorName: res
    })
  })
))

setTimeout(() => console.log(responseArray), 500)

//I would expect to have to do this:
//Promise.all(promises).then((res) => console.log(res))

我希望

setTimeout(() => console.log(responseArray), 5000) 

记录一个空字符串,因为承诺数组尚未通过 Promise.all 运行,但看起来即使地图应该只是返回一个承诺数组,它实际上正在运行承诺。这是为什么呢?

编辑

我已经编辑了 getAuthor 承诺,等待一秒钟,然后再进一步详细说明,因为这不是我想要达到的目的。

我希望对数组进行映射,以返回一个新数组,其中包含映射中返回的任何内容。例如,如果我这样做

let arrayOfPromises = books.map((book) => {
   return getAuthor(book.AuthorId)
}

我希望 arrayOfPromises 是一个 getAuthor 承诺的数组。

但是,当我在返回的 Promise 末尾抛出 .then() 时,似乎 .then() 中的代码正在评估。

如果我这样做了

let promises = books.map((book) => {
   return getAuthor(book.AuthorId).then((res) => {
      console.log(res)
   })
}

在控制台中我会看到“Herman Merville”和“F. Scott Fitzgerald”,在 promises 变量中我会看到一系列的 promises。

虽然我认为每个 getAuthor 的 .then 只会在我调用 Promise.all(promises) 时进行评估,因为 getAutor 承诺会在地图内返回。我在这里理解有什么问题吗?

【问题讨论】:

  • 承诺就是这样,对未来的承诺。然而,它可能已经发生了。例如,我可以给你Promise.resolve(5),这是对值 5 的承诺。它已经“运行”了。

标签: javascript


【解决方案1】:

这是因为你的 Promise 中的代码不是异步的,所以它会立即解析,因为没有什么可等待的。

为了扩展一点,你正在做的是一样的

const getAuthorName = (AuthorId) => {
  return Promise.resolve(authors.find((author) => {
    return author.AuthorId === AuthorId
  }).Name)
}

它将允许您在函数上链接then 语法,但这也意味着then 将在函数调用之后直接执行。

编辑

根据您的编辑,这里还有更多内容。

您已使用setTimeout 将方法修改为异步。这意味着地图现在按您的预期返回一系列承诺。

但是,您的代码还有另一个问题,您还将console.log 封装在setTimeout 中,一个更大的计时器!

为了更好地了解这里发生了什么,我强烈建议您观看this video

不过本着 StackOverflow 的精神,我会在这里添加一些书面说明。

您首先要创建一个 promise 数组,在创建后大约 1 秒后执行回调(它们不会等待地图结束)。

一旦完成,它们就会进入浏览器的所谓事件循环。基本上,一旦主线程被释放,浏览器就会处理一堆事件。

因此,在它们创建后 1 秒,promise 将解析,并将它们的回调放在这个 事件循环上。

现在,当您运行 console.log 时会发生什么。

如果你不把它放在它自己的setTimeout 中,它会打印一个空数组。

如果setTimeout 为 500 毫秒怎么办?在这里,我们可以安全地假设map 将花费不到 500 毫秒的时间来完成并启动新的setTimeout,因此console.log 将首先到达 事件循环 并再次打印一个空数组。

5s 呢? setTimeouts 都将在到达 console.log 之前很久,因此您将打印一个填充的数组。

如果您想自己尝试一下,我创建了jsfiddle

我希望这有助于解决问题。

【讨论】:

  • 我已经编辑了我的原始问题,这有助于详细说明问题吗?
  • @AndreFuller 我进行了编辑以更好地回答您的问题。
猜你喜欢
  • 2017-01-19
  • 1970-01-01
  • 2021-05-31
  • 2022-01-15
  • 1970-01-01
  • 1970-01-01
  • 2016-03-28
  • 2020-01-12
  • 2021-11-09
相关资源
最近更新 更多