【问题标题】:Issue with Promise not getting resolved with maximum retries attempt最大重试尝试无法解决 Promise 的问题
【发布时间】:2020-06-25 10:28:41
【问题描述】:

过去几天我一直在与 Promise 和一个初学者一起努力。我必须调用外部 API 并检查是否有响应。如果为空,则以最大重试次数再次调用 api,这是可配置的。我尝试了以下方法,但不确定我在这里缺少什么。这是我所做的一个示例。

如果数据可用,则显示数据可用。如果没有,则重试一段时间,如果达到零,则使用 API 中不可用的数据进行解析。如果重试后数据可用(例如:第二次),则停止并显示响应。

function callExternalAPI(retry, interval) {
 try {
    return new promise((resolve, reject) => {
        if(retry <=0 ) {
            return resolve("Data not available in API"); // This is not working, request is hanged.
        }
        return myapiCall().then((response) => {
           if(!response) {
              // If empty then retry calling api again
              return setTimeOut(async () => {
                 await callExternalAPI(retry - 1, interval)
              }, interval);
           }
           return resolve(response);
        })
    })
}
   } catch((error) => {
       reject(error);
    })



callExternalAPI(3, 1000).then((rsp) => {
   console.log({response: "data available"});
}).catch((error) => {
  throw error;
})

任何帮助将不胜感激,因为我仍处于学习阶段。

提前致谢

【问题讨论】:

  • 不确定,但在 myapicall 回调中,您使用 response 作为变量名并且您正在检查 if(!result),因此请确保两者相同。
  • @RajShah 在 S.O. 中询问时这是一个错字
  • 如果你是一个初学者,你会发现这些教程很有帮助:tutorama.info/CTG/Promises

标签: javascript node.js promise es6-promise


【解决方案1】:

一种方法是将尝试之间的“等待”包装到单独的async 方法和await 此方法中。

然后,只要重试计数未达到0,如果没有从myapiCall 返回成功结果,则简单地重复callExternalApi。请注意,在我的示例中,如果我们达到最大计数,我将抛出一个错误(当然,您可以以不同的方式处理这个问题,例如返回 undefined/null):

async function wait(timeInMs) {
  console.log('Waiting ...');
  return new Promise((resolve => setTimeout(() => resolve(), timeInMs)));
}


async function callExternalApi(numberOfTries, timeout) {
  if (numberOfTries <= 0) {
    return "Data not available in API";
  }

  const result = await myapiCall();

  if (result) {
    return result;
  }

  await wait(timeout); // wait for the defined timeout before recurring

  return callExternalApi(numberOfTries - 1, timeout);
}

(async () => {
   try {
     const result = await callExternalApi(3, 1000);
     console.log(result);
   } catch(err) {
     console.log(err);
   }
})();

【讨论】:

  • 如果达到最大重试次数,那么我需要返回“数据不可用”之类的内容并且不想抛出。
  • @Vishnu:已修复,请再次检查。
  • 当然我会尝试,只是渴望我们如何在 Promise 中实现相同的功能。 :)
  • @Vishnu:如果是出于学习目的,那很好,对于生产代码,您绝对应该使用 async/await,因为它允许更清晰的代码,正如您在我的示例中看到的那样。
【解决方案2】:

我建议使用async / await 语法,这使我们能够以更易于理解的形式构建代码,一旦我们这样做,创建一个包装函数相对简单,调用ExternalAPI 来循环API 调用。

等待函数在每次失败后被调用,你可以让它更复杂,每次尝试可能会后退更长的时间,但我们现在将保留一个恒定的时间间隔。

// Let's make this fail on the first couple of calls.
let myApiCallCount = 0;

// Mock function to simulate an API call. You can replace this with a real api call.
function myapiCall() {
    if (++myApiCallCount < 3) {
        return new Promise((resolve, reject) => setTimeout(reject, 500, new Error("Some API error")));
    }
    return new Promise(resolve => setTimeout(resolve, 500, { users: [ { name: "user1"},{ name: "user"} ]}));
}

function wait(interval) {
    return new Promise(resolve => setTimeout(resolve, interval));
}

async function callExternalAPI(apiFunction, attemptCount, interval) { 
    for(let attempt = 1; attempt <= attemptCount; attempt++) { 
        console.log(`callExternalAPI: Calling ${apiFunction.name}, attempt ${attempt} of ${attemptCount}`);
        try {
            let response = await apiFunction();
            // Check if response is null.
            if (response) {
                console.log(`callExternalAPI: Success: Received response: ${JSON.stringify(response)}, exiting loop...`);
                return response;
            }
        } catch (error) {
            console.error(`callExternalAPI: Error occurred: ${error}`);
        }
        // Wait for <interval> milliseconds.
        await wait(interval);
    }
    return null;
}

callExternalAPI(myapiCall, 3, 1000);

【讨论】:

    【解决方案3】:

    关于您的代码的一些评论。 您的 try catch 未正确实现,代码将无法执行。 您没有检查是否存在 response,因为您使用的是 result 在这一行myapiCall().then((response) =&gt; { if(!result) ... 它应该是myapiCall().then((response) =&gt; {if(!response),尽管在检查 api 响应时应该小心,因为例如 fetch 不处理错误,所以在这种情况下你应该检查响应状态response.status==200,否则它会始终默认为 true,同样在您的示例中,您不需要使用 setTimeout,您可以只检查响应,如果没有响应,则再次调用该函数。 优化你的代码我相信它应该是这样的

        i=0
    function callExternalAPI(retry, interval) {
         return new Promise((resolve, reject) => {
             return myapiCall().then((response) => {
               if (response) return response  //if you are using fetch / axios....... use response.status==200
                if(!response) {   //if you are using fetch / axios .... use response.status!=200
                        while(i<retry){
                          setTimeout(async()=>{
                            retry--
                       return await callExternalAPI(retry,interval)  
                          },interval)
                            i++              
                        }
                      if(i>retry) console.log("Data Not Available From This Api")
                }
             })
         })
     }
     callExternalAPI(3, 1000).then((rsp) => {
        console.log(rsp);
     }).catch((error) => { 
       throw error;
     })
    

    【讨论】:

    • @sven-hig:感谢您的反馈。问题是我必须调用 api 特定的次数。 IE;如果客户输入为 3,则最大重试次数将为 3,如果结果为空。这就是我使用 setTimeout 的原因。请随时发表评论。
    • 我已经更新了代码以考虑到这一点,试试看它是否适合你
    • @sven-hig 当响应为空时我们还需要其他情况吗?因为我们已经在代码中检查响应是否存在。如果我错了,请纠正我。
    • 如果你的意思是响应是 null 一个空对象,不,你不必因为它被认为是一个成功的响应,你可能会向客户端显示一条消息“没有可用的数据”,我已经对代码进行了一些修改并进行了测试,它应该可以工作,检查一下,让我知道它是否适合你,还有什么问题可以随时提出
    • @sven-hig 是的,我的意思是 if(response) 和 else return response 都在做同样的事情,对吗?所以它是冗余的。
    【解决方案4】:

    return setTimeOut() 出现的地方,您需要返回一个setTimeOut() 没有提供的Promise .....。

    诀窍在于知道如何“承诺”setTimeOut()

    function delay(ms) {
        return new Promise(resolve => {
            setTimeout(resolve, ms);
        });
    }
    

    有了delay(),你可以写:

    function callExternalAPI(retry, interval) {
        return myapiCall()
        .then(response => {
            if(response) {
                return response; // Yay!
            } else {
                throw new Error('response was falsy'); // Doh!
            }
        })
        .catch((error) => { // here, catch error arising async failure of myapiCall() or from response being falsy.
            if(retry <= 0) {
                throw error; // when retries run out, rethrow last error
            } else {
                return delay(interval) // here, benefit from the promisification of setTimeout().
                .then(() => callExternalAPI(retry - 1, interval));
            }
        });
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 1970-01-01
      • 2019-08-31
      • 1970-01-01
      • 2023-01-12
      • 2019-03-16
      • 2014-09-18
      相关资源
      最近更新 更多