最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Async function inside for loop in React - Stack Overflow

programmeradmin1浏览0评论

I have this piece of code in React which calls the Dark Sky API and returns weather data for a specific day. Each request gets a particular date. Once the request is successful, I put the data in an array via setState. The code runs fine but each request runs asynchronously so the data gets pushed to the array in the order in which each call pletes so it's not getting me an ordered day list. Here's the code:

loadPreviousMonth = async () => {
  const current = {...this.state};

  for (let i=30; i >= 1; i--) {
    const date = new Date();
    const previousDay = Math.floor(date.setDate(date.getDate() - i) / 1000);
    const url = `/api/darksky?latitude=${current.latitude}&longitude=${current.longitude},${previousDay}`;
    await darkSky(url, this.onTimeRequestSuccess, this.onError);
  }
}

Thanks!

I have this piece of code in React which calls the Dark Sky API and returns weather data for a specific day. Each request gets a particular date. Once the request is successful, I put the data in an array via setState. The code runs fine but each request runs asynchronously so the data gets pushed to the array in the order in which each call pletes so it's not getting me an ordered day list. Here's the code:

loadPreviousMonth = async () => {
  const current = {...this.state};

  for (let i=30; i >= 1; i--) {
    const date = new Date();
    const previousDay = Math.floor(date.setDate(date.getDate() - i) / 1000);
    const url = `/api/darksky?latitude=${current.latitude}&longitude=${current.longitude},${previousDay}`;
    await darkSky(url, this.onTimeRequestSuccess, this.onError);
  }
}

Thanks!

Share Improve this question edited Feb 21, 2019 at 13:43 Shubham Khatri 282k58 gold badges430 silver badges411 bronze badges asked Feb 21, 2019 at 13:32 ArielAriel 1231 gold badge2 silver badges9 bronze badges 4
  • 1 shouldn't you save the data in your state unordered, and order it after retrieving from state but before showing to user . Ordering array of 30 items will take just a few milliseconds. But making next api call only after receiving current will take forever to make 30 api calls. – Vaibhav Vishal Commented Feb 21, 2019 at 13:47
  • I could do that but I was hoping I can run each API call in order so I don't have to take that extra step. – Ariel Commented Feb 21, 2019 at 13:50
  • You should take that extra step, edited my previous ment to add 'why'. – Vaibhav Vishal Commented Feb 21, 2019 at 13:50
  • Thanks I ended up just sorting the array after :) – Ariel Commented Feb 22, 2019 at 14:32
Add a ment  | 

4 Answers 4

Reset to default 5

You can use for.. of which will run in sequence:

async function processArray(array) {
  for (const item of array) {
    await darkSky(url, this.onTimeRequestSuccess, this.onError);
  }
 console.log('Done!');
}

Use Promise.all. Waaay faster than using recursion or await in a loop, as all the API calls will happen concurrently rather than sequentially.

const promises = [];

for (let i=30; i >= 1; i--) {
    const date = new Date();
    const previousDay = Math.floor(date.setDate(date.getDate() - i) / 1000);
    const url = `/api/darksky?latitude=${current.latitude}&longitude=${current.longitude},${previousDay}`;
    promises.push(darkSky(url, this.onTimeRequestSuccess, this.onError));
}

Promise.all(promises).then(arrOfResults => {
  // setState here
});

The problem is that nothing ensures you one request finish before another, but it depends on how the function darkSky it's implemented. As far as I can guess from your code it will look something like this:

function darkSky(url, onTimeRequestSuccess, onError) {
     return fecth(url).then(onTimeRequestSuccess).catch(onError);
}

Because you're using onTimeRequestSuccess as a resolving on the promise inside darkSky the await in processArray is not going to wait for the fetch promise, instead is going to wait for the result of onTimeRequestSuccess. That's why the order is not maintained.

If you want to keep the order (without reordering after getting the data) and not having a chain of 30 consecutive calls, one approach could be create an array with the promises of all the queries, wait for all to be resolved using Promise.all and then setting the whole array to the state. Eg:

loadPreviousMonth = () => {
  const current = {...this.state};
  const promises = [];

  for (let i=30; i >= 1; i--) {
    const date = new Date();
    const previousDay = Math.floor(date.setDate(date.getDate() - i) / 1000);
    const url = `/api/darksky?latitude=${current.latitude}&longitude=${current.longitude},${previousDay}`;
    promises.push(fetch(url));
  }

  Promise.all(promises).then((results) => this.setState({results: results}));
}

You could try recurssive function call. something like this:

var delay = ms => new Promise(res => setTimeout(res, ms));



    function executeInOrder(iteration){
        if(iteration>0){
            delay(500).then(()=>{
                console.log(iteration)
                executeInOrder(iteration-1)
            })
        }
    }
    
  executeInOrder(30)  
But as mentioned in ments, calling all and then sorting will be faster.

发布评论

评论列表(0)

  1. 暂无评论