这是一篇ChatGPT写的博客:vue3&js坑——在循环中发起网络请求,返回结果只有最后一次网络请求的数据


在开发过程中,本想记录遇到的一个bug,自己写完博客之后,突发奇想,请 ChatGPT 帮我写了一篇同样内容的博客。

在这里插入图片描述

我自己写的

场景

我需要在循环中发起网络请求,每次循环都把网络请求返回来的数据存入前端数据库RxDB中。我使用forEach方法,来实现:

this.befDataview.forEach(async (i)=>{
    
    
	const dbDataview=await api.insertDataview(this.afterDatasetId,aftDataview)
	const result=await this.$rxdb dataview.insert(dbDataview)
})

上述代码是一个3次循环,每次循环都向后端发起一次网络请求,然后将请求得到的数据 dbDataview 插入到rxdb 中。

bug

上面的代码运行后,rxdb 报错:不能重复插入相同主键(id)的文档。经过调试发现,在3次循环中,每次都会把最后一次网络请求来的数据插入到 rxdb 中,也就是说,我3次循环后返回的结果,只包含最后一次网络请求来的数据。

解决

我查看了 network 中的请求,发现每次后端返回的数据都是对的,所以问题只能出在前端。依稀感觉是异步这一块出了问题,却又不知道到底出在哪,又如何解决。问了 ChatGPT 终于找到了问题所在。

这是由于forEach方法不会等待async函数的返回值,所以它会继续执行下一个循环,而不会等待当前的异步操作完成。因此,我的代码中的rxdb.insert()方法会在所有网络请求完成之后只插入最后一次请求返回的数据。

应该使用for...of循环或Promise.all()方法来解决这个问题。for...of循环会等待每个await表达式完成后再进行下一次迭代,而Promise.all()可以等待所有异步操作完成后才继续执行下一步操作。

以下是使用for...of循环的示例代码:

for(const i of this.befDataview){
    
    
  const dbDataview = await api.insertDataview(this.afterDatasetId, aftDataview);
  const result = await this.$rxdb.dataview.insert(dbDataview);
}

以下是使用Promise.all()方法的示例代码:

await Promise.all(this.befDataview.map(async i => {
    
    
  const dbDataview = await api.insertDataview(this.afterDatasetId, aftDataview);
  const result = await this.$rxdb.dataview.insert(dbDataview);
}));

结论

在我的代码中,网络请求以及rxdbinsert操作是写在同一个循环中的,但它们的执行顺序却不是我期望的。

具体地说,forEach循环会在循环体中启动多个异步操作,即多个网络请求和多个rxdbinsert操作。由于forEach方法不会等待async函数的返回值,所以它会立即进行下一次迭代,而不会等待当前的异步操作完成。这就意味着,如果我在循环体中使用了await表达式,它只会等待当前这个异步操作完成后才会继续执行下一步操作,而不会等待所有异步操作完成后再进行下一次迭代。

因此,在我的代码中,网络请求的结果和rxdbinsert操作的执行顺序是不确定的,取决于每个异步操作的响应时间和处理时间。通常情况下,最后一个网络请求的结果会最后插入到rxdb中。

为了确保每个异步操作都能按照我期望的顺序执行,并且不会受到其他操作的影响,我应该使用for...of循环或者Promise.all()方法,以保证每个异步操作都能依次执行,并且等待所有异步操作完成后再进行下一步操作。

上面是我自己总结的博客,写完之后我突发奇想,为什么不直接让 ChatGPT 帮我写一篇博客呢?于是就有了下面的内容。

ChatGPT帮我写的

题目:如何避免Vue3中在循环中发起网络请求导致的bug

在Vue3中,我们经常需要在循环中发起网络请求来处理数据。然而,在这样的场景中,如果不小心编写代码,可能会导致一些不可预测的问题。本篇博客将介绍如何避免在循环中发起网络请求导致的问题,并提供一些实用的技巧来帮助你写出更加健壮的代码。

问题描述

最近我遇到了一个问题:我需要在Vue3中使用forEach循环来处理一个数据集合,同时在循环体内发起网络请求,并将请求结果插入到rxdb中。然而,我发现在插入数据到rxdb中时,每次插入的数据都是网络请求最后一次返回的结果。这使得我无法正确处理我的数据,而且导致了一些不可预测的错误。

解决方法

经过一番调查和尝试,我发现这个问题的根本原因是我在forEach循环中使用了async/await表达式。由于forEach方法不会等待async函数的返回值,所以它会立即进行下一次迭代,而不会等待当前的异步操作完成。这就意味着,如果你在循环体中使用了await表达式,它只会等待当前这个异步操作完成后才会继续执行下一步操作,而不会等待所有异步操作完成后再进行下一次迭代。

为了避免这个问题,我们可以使用for...of循环或者Promise.all()方法来确保每个异步操作都能依次执行,并且等待所有异步操作完成后再进行下一步操作。例如,以下是使用for...of循环来处理数据集合,并在循环体内发起网络请求和将请求结果插入到rxdb中的示例代码:

for(const i of this.befDataview){
    
    
  const dbDataview = await api.insertDataview(this.afterDatasetId, aftDataview);
  const result = await this.$rxdb.dataview.insert(dbDataview);
}

在这个示例中,我们使用for...of循环来依次处理数据集合中的每个元素。在循环体内,我们首先发起网络请求来获取数据,然后将获取到的数据插入到rxdb中。由于我们使用了await表达式,它会等待当前的异步操作完成后才会进行下一次迭代,这就保证了每个异步操作都能依次执行,并且等待所有异步操作完成后再进行下一步操作。使用这种方式,我们可以避免在循环中发起网络请求导致的问题,并保证代码的正确性和健壮性。

除了使用for...of循环外,我们还可以使用Promise.all()方法来处理异步操作。Promise.all()方法接收一个由多个Promise对象组成的数组,并返回一个新的Promise对象。这个新的Promise对象将在所有Promise对象都成功完成时被解析,并将解析后的值存储在一个数组中。例如,以下是使用Promise.all()方法来处理异步操作的示例代码:

await Promise.all(this.befDataview.map(async i => {
    
    
  const dbDataview = await api.insertDataview(this.afterDatasetId, aftDataview);
  const result = await this.$rxdb.dataview.insert(dbDataview);
}));

在这个示例中,我们首先使用map()方法将数据集合中的每个元素映射成一个Promise对象,并将这些Promise对象存储在一个数组中。然后,我们使用Promise.all()方法来等待所有Promise对象都成功完成后,将它们的返回值存储在一个数组中。使用这种方式,我们可以并行地执行异步操作,并在所有操作完成后才进行下一步操作,这有助于提高代码的性能和效率。

总结

在Vue3中,在循环中发起网络请求可能会导致一些不可预测的问题,例如数据不正确或者代码运行异常。为了避免这个问题,我们可以使用for...of循环或者Promise.all()方法来确保每个异步操作都能依次执行,并且等待所有异步操作完成后再进行下一步操作。这样可以提高代码的正确性和健壮性,同时也有助于提高代码的性能和效率。

这篇文章到这里就结束了,不得不说 ChatGPT 的语言表达能力太强了,描述问题清晰而准确,相比之下,我写的仅仅能称之为笔记,而不是博客。

猜你喜欢

转载自blog.csdn.net/weixin_45605541/article/details/130016646