【问题标题】:Recursive awaited promise does nothing after resolving递归等待的承诺在解决后什么都不做
【发布时间】:2021-06-03 06:45:18
【问题描述】:

我正在向 Twitter API 发出一些请求,为了检索推文,我需要执行一些递归方法调用,因为每个请求最多只能返回 100 条推文。

所以这个过程很简单。

  1. 调用函数并等待它
  2. 执行 http 请求,等待
  3. 如果响应的元数据包含 next_token,则缓存结果并使用下一个令牌执行另一个 http 请求。
  4. 重复直到 next_token 未定义,此时使用所有推文列表解决承诺。

然而这并没有按预期工作,递归 http 工作,但是当递归函数的 else 块满足并且 promise 被解决时,什么也没有发生。执行不会回到第一个函数。一切似乎都在旋转,什么也不做。我在每一行都添加了断点,但没有任何东西会触发断点。

我哪里错了?

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)
            }
        })
    }

替代实现 - 相同问题

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)
            }
        })
    }

【问题讨论】:

  • 随意观察是只有最终的承诺解决了,其他的只是结束。但是我对手动创建的承诺链接在一起有点不稳定。
  • 你不应该也await 打电话给this.getAllTweetsRecursively(ticker, newNextToken, tweetList); 吗?
  • @thedude 我已经根据您的建议添加了一个新的实现。问题依然存在
  • @tmdesign 见上文
  • 要么将函数标记为async,然后返回正常值,要么将其设为常规函数并返回 Promise。不要两者都做:async 函数已经将结果包装为一个promose,所以你的async function(...) { return new Promise(...) } 返回一个promise,它解析为promise,而不是解析为实际数据。

标签: javascript typescript asynchronous promise


【解决方案1】:

请记住,async function() {} 已经定义了一个返回 Promise 的函数,因为这就是 async 关键字为您所做的。它通过将函数体包装在 Promise 中来使您的函数体异步,而无需您自己编写 Promise 代码。然后为方便起见,await 关键字等价于 Promise.then(),无需自己编写提取代码。

因此,以下两个函数相同

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

async function() {
  return 4;
}

两者都返回一个 Promise,两者都可以是 awaited 在另一个 async 函数中,并且两者都可以是 .then() 链式的,因为它们实际上是同一个东西。所以对于你的代码,如果你想使用async/await,你不应该也构建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));
    }

    // no need for "else": 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
  })
}

您的代码包含 async getAllTweetsRecursively(...) { return new Promise(async (resolve, reject) =&gt; { ... })) 现在将返回三个嵌套的承诺,因为这些 async 关键字使得:

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

因此async 进一步展开为:

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

这种方式是疯狂的=)

【讨论】:

    【解决方案2】:

    问题是内部承诺在递归的情况下没有解决 称自己。

    添加resolve(await this.getAllTweetsRecursively(ticker, newNextToken, tweetList)); 解决了这个问题。

    public async getTweetList(ticker: string): Promise<string[]> {
      let tweets: string[] = [];
      return await this.getAllTweetsRecursively(ticker, null, 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);
          })
          resolve(await this.getAllTweetsRecursively(ticker, newNextToken, tweetList));
        }
        else {
          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);
          });
          resolve(cleanedTweets)
        }
      })
    }
    

    【讨论】:

    • 除了你还在混合async 和“returning a Promise”,所以你仍然想解决这个问题。 async 关键字是语法糖,它使应用它的函数将其返回值包装在 Promise 中,因此 async function() { return new Promise() }function() { return new Promise(() =&gt; new Promise()); } 相同:不要混合它们。要么使用 asyncawait,在任何地方都没有承诺,或者使用带有 .then() 链接的承诺。
    • 所以我不应该在等待的函数中使用 .then() 吗?
    • 没有理由在异步函数中使用.then(),只需使用await 捕获结果。但是,在常规函数中(没有async 关键字),您不能使用await(因为await 只能在async 函数中使用),因此您需要在这些函数中使用.then()案例。
    猜你喜欢
    • 2019-04-29
    • 1970-01-01
    • 2018-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-04
    相关资源
    最近更新 更多