Promise 的问题无法通过最大重试尝试得到解决

Issue with Promise not getting resolved with maximum retries attempt

过去几天我一直在为 Promise 和初学者而苦苦挣扎。我必须调用外部 API 并检查是否有响应。如果为 null,则以最大重试次数再次调用 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;
})

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

提前致谢

一种方法是将尝试之间的“等待”包装到单独的 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);
   }
})();

我建议使用 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);

关于您的代码的一些评论。 您的 try catch 未正确实现,代码将无法执行。 您没有检查是否有 response 因为您使用了 result 在这一行 myapiCall().then((response) => { if(!result) ... 中,它应该是 myapiCall().then((response) => {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;
 })

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