skip to Main Content

I’m trying to fetch data from array every two seconds and then send the request to the twitter API to follow the user. The problem is that after I run the script it instantly throws me all 400 users and then after two seconds another one is being thrown. How can I fix this so I see every user followed after two seconds and not see all those 400 thrown at the script start?

const params = {screen_name: 'user'};
client.get('friends/ids', params, function(error, tweets, response) {
  if (!error) {
      const body = JSON.parse(response.body);
      body.ids.slice(0, 400).forEach((element) => 
             setInterval( () => {
                client.post('friendships/create', {user_id: element}, function(error, tweets, response){
                    console.log(`User ${element} followed.`);
                })
            }, 2000)
        );
  }
});

3

Answers


  1. You can use libraries for this like Async or Bluebird

    Or can do it like this:

    const params = {
      screen_name: 'user'
    };
    
    function createFriendship(user_id, callback) {
      client.post('friendships/create', { user_id }, callback);
    }
    
    function processUsers(ids) {
      const userId = ids.shift();
    
      createFriendship(userId, (error, tweets, response) => {
        if (error) {
          /// Handle error here
        }
    
        console.log(`User ${userId} followed.`);
    
        if (ids.length) setTimeout(() => processUsers(ids), 2000);
      })
    }
    
    client.get('friends/ids', params, function (error, tweets, response) {
      if (!error) {
        const body = JSON.parse(response.body);
    
        processUsers(body.ids.slice(0, 400))
      }
    });
    
    Login or Signup to reply.
  2. The problem here is, that you register all async-tasks with the setInterval function on the macro-thread at the same time. Because they all have a delay of 2 seconds you’ll see them all execute after this time.

    id1: x-----------t-----------t----->
    id2: x-----------t-----------t----->
    ...
    400
         ^           ^           ^
         |           |           here all tasks are executed again
         |           here all tasks are executed
         here you register all intervals
    

    In order to emit users one by one with a 2 seconds delay you could make use of RxJS.

    import { of, interval } from 'rxjs';
    import { delay, tap, takeUntil } from 'rxjs/operators';
    
    client.get('friends/ids', params, (error, tweets, response) => {
      if (error) {
        return;
      }
      const body = JSON.parse(response.body);
      const interval$ = interval(2000);
      const maxReached$ = interval$.pipe(filter(index => index > 400));
    
      zip(interval$, of(...body.ids)).pipe(
        takeUntil(maxReached$),
        tap(([_, id]) => {
          client.post('friendships/create', {user_id: element}, () => console.log('err'))
        })
      ).subscribe();
    });
    

    You can even go further and create your own observable from the client.get function. This makes your code very clean. See the stackblitz.

    Login or Signup to reply.
  3. If you want to avoid using any libraries:

    setInterval will wait 2 seconds to handle the script it contains but the rest of the code will keep going. This means the forEach loop will execute 400 times and set 400 different intervals of 2 seconds, which will end around the same time.

    What you could use instead is something like:

    const delayLoop(arr, delay, incr = 0) {
        if (incr < arr.length) {
            incr = incr + 1;
            setTimeout( () => {
                client.post('friendships/create', {user_id: arr[incr]}, function(error, tweets, response){
                    console.log(`User ${arr[incr]} followed.`);
                })
                delayLoop(arr, delay, incr);
            }, delay)
        }
    }
    
    client.get('friends/ids', params, function(error, tweets, response) {
      if (!error) {
          const body = JSON.parse(response.body);
          let idArray = body.ids.slice(0, 400);
          delayLoop(idArray, 2000, 0);
      }
    });
    

    Note: I just wrote this off the top of my head so it may have some errors if you try and run it but you get the gist of it. You essentially create a recursive loop that iterates through your data. It can be made even more generic than this, or less so if you want the delay to be fixed.

    This code will fetch all 400 users instantly an then 2 seconds to post each user, 800 seconds in total. If what you want is to “get” the users every 2 seconds, you should place the get request in a setTimeout() or setInterval()

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search