Search code examples
javascriptnode.jsapipoststream

nodejs/javascript on stream data post loop delay


I am trying to use a twitter npm to search for tweets in realtime and like them. It streams the tweets data and then uses .post to create the likes.

Currently works but I keep running into 429 too many request errors because of the api rate limit. Ive been trying to get it to pause after each like, however nothing I've tried seems to work. At most it effects the loop before or after but never in between the post/like action.

Any ideas how to get it to delay after each post(like)? I've commented out some of the things I've already tried.

// Returns a Promise that resolves after "ms" Milliseconds
const timer = ms => new Promise(res => setTimeout(res, ms))
const wait = (duration, ...args) => new Promise(resolve => {
    setTimeout(resolve, duration, ...args);
 });

function LikeTweets() {   
    client.stream('statuses/filter', { track: terms }, function (stream) {              
        stream.on('data', async function (tweet) {    
                    
           // try {           
               // for (var i = 0; i < 3;) {
                v1Client.post('favorites/create', { id: tweet.id_str })
                .then(async (result) => {                    
                    console.log(result.text);                     
                    i++;                      
                    console.log(i); 
                    await timer(10000);              
                }).then(async (newresult) => {                    
                    console.log(newresult);
                    await timer(10000);               
                }).catch(error => {                      
                    console.log(error);
                    return;    
                });
                //await timer(3000); // then the created Promise can be awaited
           // }
           // } catch(err) {               
           //     console.log("or catching here?");  
                
            //    setTimeout(function() {               
            //        LikeTweets();
            //    }, 15000); 
          //  }
           
        });
    
   
    });
}

setTimeout(function() {               
    LikeTweets();
}, 15000);

Solution

  • You make one request per invocation of the stream.on("data", ...) event handler, therefore if 100 data events arrive within a minute, you will make 100 requests within that minute. This exceeds the rate limit.

    You must ensure that the sequence of requests made is slower than the sequence of incoming events. The following code illustrates how this decoupling of sequences can be achieved:

    /* Make one request every 20 seconds. */
    var requestQueue = [];
    function processQueue() {
      var r = requestQueue.shift();
      if (r) v1Client.post("favorites/create", r.payload).then(r.resolve, r.reject);
      setTimeout(processQueue, 20000);
    }
    processQueue();
    
    /* Use this function to schedule another request. */
    function makeRequest(payload) {
      var r = {payload};
      requestQueue.push(r);
      return new Promise(function(resolve, reject) {
        r.resolve = resolve;
        r.reject = reject;
      });
    }
    
    stream.on("data", function(tweet) {
      makeRequest({id: tweet.id_str}).then(async (result) => {...});
    });
    

    The promise returned by makeRequest can take a while until it resolves, therefore the code {...} may be executed only after several seconds or even minutes. In other words: The code uses the power of promises to keep within the strictures of the API rate limit.

    This works only if, in the long run average, the number of incoming data events does not exceed the possible rate of outgoing requests, which is 1 every 20 seconds. This is nothing you can get around without a mass-update API (which would not be in the interest of the Twitter community, I assume).