After being awaited recursively, the resolved promise does not perform any actions

When working with the Twitter API, I need to make recursive method calls to retrieve tweets since each request only returns a maximum of 100 tweets.

The process is straightforward:

  1. Call the function and await it
  2. Make an HTTP request and await that
  3. If the response metadata contains a next_token, cache the results and make another HTTP request using the next token.
  4. Repeat until the next_token is undefined, then resolve the promise with the list of all tweets.

However, something seems to be going wrong. The recursive HTTP calls work fine, but when the else block in the recursive function is executed and the promise is resolved, nothing happens. Execution doesn't return to the initial function. It's almost like everything is just spinning without progress. Even setting breakpoints on every line does not trigger any breakpoints.

What could be causing this issue?

public async getTweetList(ticker: string): Promise<string[]>{
        let tweets: string[] = [];

        tweets = await this.getAllTweetsRecursively(ticker, null, tweets);
        return tweets;
    }

    public async getAllTweetsRecursively(ticker: string, nextToken: string, tweetList: string[]): Promise<string[]>{
        return new Promise(async (resolve, reject) => {
            let query = `?query=(${ticker})`
            query += this.extraQuery;

            if(nextToken){
                query += this.nextTokenQuery + nextToken
            }

            let res = await axios.default.get(this.url + query, {
                headers: this.headers
            })

            let newNextToken = res.data.meta.next_token;
            if(res.data.data.length > 0 && newNextToken){
                res.data.data.forEach(tweet => {
                    tweetList.push(tweet.text);
                })
                this.getAllTweetsRecursively(ticker, newNextToken, tweetList);
            }
            else {
                resolve(cleanedTweets)
            }
        })
    }

Alternatively, here's another implementation facing the same issue:

public async getTweetList(ticker: string): Promise<string[]>{
        return new Promise(async (resolve) => {
            let tweets: string[] = [];

            tweets = await this.getAllTweetsRecursively(ticker, null, tweets);
            resolve(tweets);
        })

    }

    public async getAllTweetsRecursively(ticker: string, nextToken: string, tweetList: string[]): Promise<string[]>{
        return new Promise(async (resolve, reject) => {
            let query = `?query=(${ticker})`
            query += this.extraQuery;

            if(nextToken){
                query += this.nextTokenQuery + nextToken
            }

            let res = await axios.default.get(this.url + query, {
                headers: this.headers
            })

            let newNextToken = res.data.meta.next_token;
            if(res.data.data.length > 0 && newNextToken){
                res.data.data.forEach(tweet => {
                    tweetList.push(tweet.text);
                })
                await this.getAllTweetsRecursively(ticker, newNextToken, tweetList);
            }
            else {
                let cleanedTweets: string[] = [];
                tweetList.forEach(tweet => {
                    if(tweet.startsWith("RT")){
                        return;
                    }
                    if(!tweet.toLowerCase().includes("$" + ticker)){
                        return;
                    }
                    cleanedTweets.push(tweet);
                });
                resolve(cleanedTweets)
            }
        })
    }

Answer №1

The problem arose when the inner promise failed to resolve during its recursive call.

The issue was resolved by including

resolve(await this.getAllTweetsRecursively(ticker, newNextToken, tweetList));
.

public async getTweetList(symbol: string): Promise<string[]> {
  let tweets: string[] = [];
  return await this.getAllTweetsRecursively(symbol, null, tweets);
}

public async getAllTweetsRecursively(symbol: string, nextToken: string, tweetList: string[]): Promise<string[]>{
  return new Promise(async (resolve, reject) => {
    let query = `?query=(${symbol})`
    query += this.additionalQuery;

    if(nextToken){
      query += this.nextTokenParam + nextToken
    }

    let response = await axios.default.get(this.apiUrl + query, {
      headers: this.requestHeaders
    })

    let newNextToken = response.data.meta.next_token;
    if(response.data.data.length > 0 && newNextToken){
      response.data.data.forEach(tweet => {
        tweetList.push(tweet.text);
      })
      resolve(await this.getAllTweetsRecursively(symbol, newNextToken, tweetList));
    }
    else {
      response.data.data.forEach(tweet => {
        tweetList.push(tweet.text);
      })
      let filteredTweets: string[] = [];
      tweetList.forEach(tweet => {
        if(tweet.startsWith("RT")){
          return;
        }
        if(!tweet.toLowerCase().includes("$" + symbol)){
          return;
        }
        filteredTweets.push(tweet);
      });
      resolve(filteredTweets)
    }
  })
}

Answer №2

Always keep in mind that when you use async function() {}, it automatically creates a function that returns a Promise. Essentially, the async keyword handles the asynchronous nature of the function by encapsulating it in a Promise without requiring you to manually write Promise code. Additionally, the await keyword simplifies handling Promises compared to using Promise.then() as it eliminates the need for extraction code.

In essence, the following two functions are essentially the same:

function() {
  return new Promise(resolve, reject => {
    resolve(4);
  });
}

async function() {
  return 4;
}

Both functions return Promises, can be awaited inside another async function, and can be chained with .then(). This is because they are effectively identical. Hence, if you intend to utilize async/await in your code, there's no necessity to construct additional Promises:

public async getTweetList(ticker: string): Promise<string[]> {
  return await this.getAllTweetsRecursively(ticker, null, []);
}

public async getAllTweetsRecursively(ticker: string, nextToken: string, tweetList: string[]): Promise<string[]>{
    let query = `?query=(${ticker})${this.extraQuery}`;

    if(nextToken){
      query = `${query}${this.nextTokenQuery}${nextToken}`;
    }

    let res = await axios.default.get(this.url + query, {
      headers: this.headers
    })

    let newNextToken = res.data.meta.next_token;
    if(res.data.data.length > 0 && newNextToken){
      res.data.data.forEach(tweet => tweetList.push(tweet.text))
      return await this.getAllTweetsRecursively(ticker, newNextToken, tweetList));
    }

    // There's no need for an "else" statement here as the "if" already returns.

    res.data.data.forEach(tweet => tweetList.push(tweet.text))
    let cleanedTweets: string[] = [];
    tweetList.forEach(tweet => {
      if(tweet.startsWith("RT")) return;
      if(!tweet.toLowerCase().includes("$" + ticker)) return;
      cleanedTweets.push(tweet);
    });

    return cleanedTweets
  })
}

You are currently using

async getAllTweetsRecursively(...) { return new Promise(async (resolve, reject) => { ... }))
, which results in THREE nested promises due to each "async" creating its Promise layer. This situation unfolds into:

getAllTweetsRecursively(...) {
  return new Promise(resolve, reject => {
    return new Promise(async (resolve, reject) => {
      ...
    }))
  });
}

This further complicates to:

getAllTweetsRecursively(...) {
  return new Promise(resolve, reject => {
    return new Promise((resolve, reject) => {
      return new Promise(resolve, reject => {
        resolve(...)
      })
    }))
  });
}

Remember, too many nested Promises can lead to confusion and complexity in your code! Beware of descending down this path =)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Issues with the messaging functionality of socket.io

Utilizing socket.io and socket.io-client, I have set up a chat system for users and operators. The connections are established successfully, but I am encountering strange behavior when it comes to sending messages. For instance, when I send a message from ...

RC7 is missing the necessary HTTP_PROVIDERS for the resolveAndCreate HTTP method in Angular2

During the time of RC4, I was able to create my own custom http instance using a function like this: export function createHTTP(url:string, headers?:Headers){ let injector = ReflectiveInjector.resolveAndCreate([ myHttp, {provide:'defaultUrl ...

What is the best way to specify the type of a property that has already been assigned

I am currently utilizing a third-party library that includes a type defined as the following: export interface ThirdPartyNodeType { id: string; name: string; data: any; } Upon further exploration, I have identified the content that I intend to include ...

Difficulty arises when trying to extract specific information from an ajax response using the jQuery.filter

The code snippet below seems to be causing some trouble. It's supposed to filter HTML content that includes a div with the class "filtered_entries_box", but it's not working as expected. $.ajax({ "url" : "start.php", "type" : "POST", ...

using http to handle a 404 error

This specific function is designed to fetch data under normal circumstances and to return a value of 0 in the event of a 404 error. function retrieveData(url) { if (window.XMLHttpRequest) { xmlhttp=new XMLHttpRequest(); ...

What are some effective ways to test asynchronous functions in React?

Looking for help to test async/Promise based methods in React components. Here is a simple example of a React component with async methods: import server from './server'; class Button extends Component { async handleClick() { if (await ca ...

What are the best techniques for creating animations in AngularJS?

I've been trying to figure out how to animate in AngularJS by searching online, but I haven't found a perfect solution yet. html <span class="sb-arrow down" ng-click="hideSampleList($event)"></span> ...

Delegating events after removing a class

I have a button element with the 'next' data attribute <button data-button='next' class="disabled">next</button> When I remove the disabled class to activate it, the click event doesn't trigger as expected $("[dat ...

A more concise approach to crafting ajax requests

Lately, I've been immersed in building various web applications using php, mysql, jquery, and bootstrap. Now, I'm faced with a common issue - how to streamline my ajax queries for posting data? Writing code that is not only functional but also a ...

Error: Syntax error - V token not recognized

Although this question is quite common, I am interested in understanding the theoretical reason behind it. I am attempting to send two encrypted values to my service. Javascript var encryptedlogin = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(Ema ...

What is the best way to extract specific information from an API using AJAX and Axios?

I can't figure out what I'm doing wrong when trying to access a URL from an API using Axios. For instance, I'm attempting to select the following URL: "https://media4.giphy.com/media/yy6hXyy2DsM5W/giphy-downsized.gif?cid=482277c2s3j1s8uell6v ...

Using jQuery to toggle sliding the information above a div

I am facing an issue with my customized sliding menu. The menu slides over the image but not over the content-div, pushing it aside. I have tried to fix this problem but haven't found a solution yet. My goal is for the menu to slide over all divs and ...

Cross domain request in a simple HTML document

I'm currently working on an app that is strictly in plain HTML files without a server. I'm facing difficulties with cross domain requests from JavaScript. Whenever I try to make a request, the browser displays this error message: XMLHttpRequest ...

"Encountering issues with calling a Node.js function upon clicking the button

I'm facing an issue with the button I created to call a node.js server function route getMentions, as it's not executing properly. Here is the code for my button in index.html: <button action="/getMentions" class="btn" id="btn1">Show Ment ...

Virtual machines have encountered issues when attempting to utilize setTimeout within the browser with vm.runInNewContext

When running a JS script using the vm module in a browser, the following details are included: vm.runInNewContext(codeToEval, sandboxObject); Although interval methods like setTimeout and setInterval do not work, even when exposed in the sandboxObject cr ...

Is there a way to ensure the content of two divs remains aligned despite changing data within them?

Currently, I have two separate Divs - one displaying temperature data and the other showing humidity levels. <div class="weatherwrap"> <div class="tempwrap" title="Current Temperature"> ...

When deploying, an error is occurring where variables and objects are becoming undefined

I've hit a roadblock while deploying my project on Vercel due to an issue with prerendering. It seems like prerendering is causing my variables/objects to be undefined, which I will later receive from users. Attached below is the screenshot of the bui ...

"Techniques for extracting both the previous and current selections from a dropdown menu in Angular 2

How can I retrieve the previous value of a dropdown before selection using the OnChange event? <select class="form-control selectpicker selector" name="selectedQuestion1" [ngModel]="selectedQuestion1" (Onchange)="filterSecurityQuestions($event.t ...

What is the process for testing and executing the code I've written on ACE Cloud 9?

I have integrated ACE on my website to enable users to code freely. My question is, how can I execute the Python code they write? I want to add a run button in the bottom left corner (which I can style using CSS), but I'm not sure how to actually run ...

The NextAuth file imported with the EmailProvider is currently not being utilized

Having an issue with implementing nextauth for authentication. I have imported EmailProvider from nextauth/providers but when I try to use it in the Nextauth providers object, it does not recognize the EmailProvider that I've imported. Here is the co ...