This is a blog written by ChatGPT: vue3&js pit - initiate a network request in a loop, and the returned result is only the data of the last network request


During the development process, I wanted to record a bug I encountered. After I finished writing the blog, I had a whim and asked ChatGPT to help me write a blog with the same content.

insert image description here

I wrote it myself

Scenes

I need to initiate a network request in a loop, and store the data returned by the network request into the front-end database RxDB in each loop. I use the forEach method to achieve:

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

The above code is a 3-time loop, and each loop initiates a network request to the backend, and then inserts the requested data dbDataview into rxdb.

bug

After the above code is run, rxdb reports an error: the document with the same primary key (id) cannot be inserted repeatedly. After debugging, it is found that in the 3 loops, the data from the last network request will be inserted into rxdb each time, that is to say, the result returned after 3 loops only contains the data from the last network request.

solve

I checked the requests in the network and found that the data returned by the backend is correct every time, so the problem can only lie in the frontend. I vaguely feel that there is a problem with the asynchronous part, but I don't know where it is and how to solve it. Asked ChatGPT and finally found the problem.

This is because forEachthe method will not wait for asyncthe return value of the function, so it will continue to execute the next loop without waiting for the current asynchronous operation to complete. Therefore, the method in my code rxdb.insert()will only insert the data returned by the last request after all network requests are completed.

for...ofA loop or method should be used Promise.all()to solve this problem. for...ofWhile the loop waits for each awaitexpression to complete before proceeding to the next iteration, Promise.all()it can wait for all asynchronous operations to complete before proceeding to the next step.

Here is for...ofsample code using a loop:

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

Here is Promise.all()sample code for how to use it:

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

in conclusion

In my code, network requests and rxdboperations insertare written in the same loop, but their execution order is not what I expected.

Specifically, forEachthe loop initiates multiple asynchronous operations in the loop body, that is, multiple network requests and multiple rxdboperations insert. Since forEachthe method does not wait for asyncthe return value of the function, it proceeds to the next iteration immediately without waiting for the current asynchronous operation to complete. This means that if I use awaitan expression in the body of the loop, it will only wait for the current asynchronous operation to complete before proceeding to the next step, instead of waiting for all asynchronous operations to complete before proceeding to the next iteration.

Therefore, in my code, the results of network requests and the execution order rxdbof insertoperations are indeterminate, depending on the response time and processing time of each asynchronous operation. Normally, the results of the last network request are inserted last into rxdb.

In order to ensure that each asynchronous operation can be executed in the order I expect, and will not be affected by other operations, I should use a for...ofloop or Promise.all()method to ensure that each asynchronous operation can be executed in sequence, and wait for all asynchronous operations to complete before Proceed to the next step.

The above is my own blog summary. After I finished writing it, I had a whim, why not just ask ChatGPT to help me write a blog? So there is the following content.

ChatGPT helped me write

Topic: How to avoid bugs caused by initiating network requests in loops in Vue3

In Vue3, we often need to initiate network requests in a loop to process data. However, in such scenarios, if the code is not written carefully, it may cause some unpredictable problems. This blog will introduce how to avoid problems caused by initiating network requests in a loop, and provide some practical tips to help you write more robust code.

Problem Description

Recently I encountered a problem: I need to use a loop in Vue3 forEachto process a data collection, and at the same time initiate a network request in the loop body and insert the request result into it rxdb. However, I found that when inserting data into rxdb, the data inserted each time is the result returned by the last network request. This prevents me from handling my data correctly and leads to some unpredictable errors.

Solution

After some investigation and tries, I found out that the root cause of the problem was that I forEachwas using async/awaitexpressions inside the loop. Since forEachthe method does not wait for asyncthe return value of the function, it proceeds to the next iteration immediately without waiting for the current asynchronous operation to complete. This means that if you use awaitan expression in the loop body, it will only wait for the current asynchronous operation to complete before proceeding to the next step, instead of waiting for all asynchronous operations to complete before proceeding to the next iteration.

To avoid this problem, we can use for...ofloops or Promise.all()methods to ensure that each asynchronous operation can be executed in sequence, and wait for all asynchronous operations to complete before proceeding to the next step. For example, the following is a sample code that uses for...ofa loop to process a data collection, initiates a network request and inserts the request result into the loop body :rxdb

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

In this example, we use for...ofa loop to process each element in the data collection in turn. In the loop body, we first initiate a network request to obtain data, and then insert the obtained data into rxdb. Since we use awaitan expression, it will wait for the current asynchronous operation to complete before proceeding to the next iteration, which ensures that each asynchronous operation can be executed in sequence, and waits for all asynchronous operations to complete before proceeding to the next step. Using this method, we can avoid problems caused by initiating network requests in a loop, and ensure the correctness and robustness of the code.

Instead of using for...ofloops, we can also use Promise.all()methods to handle asynchronous operations. Promise.all()The method takes an Promisearray of objects and returns a new Promiseobject. This new Promiseobject will Promisebe resolved when all objects complete successfully, and the resolved values ​​will be stored in an array. For example, here is sample code that uses Promise.all()methods to handle asynchronous operations:

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

In this example, we first use map()methods to map each element in the data collection to an Promiseobject, and Promisestore these objects in an array. We then use Promise.all()methods to wait for all Promiseobjects to complete successfully, storing their return values ​​in an array. In this way, we can execute asynchronous operations in parallel and only proceed to the next step after all operations are completed, which helps to improve the performance and efficiency of the code.

Summarize

In Vue3, making network requests in a loop may cause some unpredictable problems, such as incorrect data or abnormal code operation. To avoid this problem, we can use for...ofloops or Promise.all()methods to ensure that each asynchronous operation can be executed in sequence, and wait for all asynchronous operations to complete before proceeding to the next step. This improves the correctness and robustness of the code, while also helping to improve the performance and efficiency of the code.

This article is over here. I have to say that ChatGPT's language expression ability is too strong, and the description of the problem is clear and accurate. In contrast, what I wrote can only be called notes, not blogs.

Guess you like

Origin blog.csdn.net/weixin_45605541/article/details/130016646