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));
}
});
}
过去几天我一直在为 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));
}
});
}