1
votes

Problème avec Promise non résolu avec le nombre maximal de tentatives

Je me bats depuis quelques jours avec Promise et un débutant. Je dois appeler une API externe et vérifier si la réponse est là ou non. Si null, appelez à nouveau l'API avec un nombre maximal de tentatives, ce qui est configurable. J'ai essayé ce qui suit, mais je ne suis pas sûr de ce qui me manque ici. Voici un exemple de ce que j'ai fait.

Si des données sont disponibles, alors show data est disponible. Sinon, réessayez pendant un certain temps et si atteint zéro, résolvez avec des données non disponibles dans l'API. Si les données sont disponibles après quelques tentatives (par exemple: 2ème fois), arrêtez-vous et affichez la réponse.

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

Toute aide serait vraiment appréciée car je suis encore en phase d'apprentissage.

Merci d'avance


3 commentaires

Pas sûr, mais dans myapicall callback, vous utilisez response comme nom de variable et vous vérifiez si (! Result), alors assurez-vous simplement que les deux sont identiques.


@RajShah C'est une faute de frappe lorsqu'on lui demande dans S.O.


Si vous êtes un débutant avec les promesses telles qu'écrites dans votre message, vous trouverez peut-être ces quelques tutoriels très utiles: tutorama.info/CTG / Promesses .


4 Réponses :


2
votes

Une façon de faire ceci est d'encapsuler «l'attente» entre les essais dans une méthode async distincte et wait cette méthode.

Ensuite, répétez simplement callExternalApi si aucun résultat réussi n'a été renvoyé par myapiCall tant que le nombre de tentatives n'a pas atteint 0 . Notez que dans mon exemple, je lance une erreur si nous avons atteint le nombre maximum (vous pouvez bien sûr gérer cela différemment, comme renvoyer 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);
   }
})();


4 commentaires

Si le nombre maximal de tentatives est atteint, je dois renvoyer quelque chose comme "données non disponibles" et ne pas vouloir lancer.


@Vishnu: Correction du problème, veuillez vérifier à nouveau.


Bien sûr, je vais essayer, juste désireux de savoir comment nous pouvons mettre en œuvre la même chose dans Promise. :)


@Vishnu: si c'est à des fins d'apprentissage, c'est bien, pour le code de production, vous devez absolument utiliser async / await car cela permet un code beaucoup plus clair comme vous pouvez le voir dans mon exemple.



0
votes

Je suggère d'utiliser async / await , cela nous permet de structurer le code sous une forme plus compréhensible, une fois que nous faisons cela, il est relativement simple de créer une fonction wrapper, callExternalAPI pour boucler sur l'appel API.

La fonction wait est appelée après à chaque échec, vous pouvez le rendre plus sophistiqué, peut-être en reculant plus longtemps pour chaque tentative, mais nous laisserons un intervalle constant pour l'instant.

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


0 commentaires

0
votes

Quelques remarques sur votre code. Votre try catch n'est pas implémenté correctement et le code ne s'exécutera pas. Vous ne vérifiez pas s'il y a une réponse car vous avez utilisé le result à la place dans cette ligne myapiCall (). then ((response) => {if (! result) ... cela devrait être myapiCall (). then ((response) => {if ( ! response) , bien que vous devriez être prudent lors de la vérification de la réponse de l'API car fetch par exemple ne gère pas les erreurs donc dans ce cas, vous devriez vérifier l'état de la réponse response.status = = 200 , sinon il sera toujours par défaut true, également dans votre exemple vous n'avez pas besoin d'utiliser setTimeout, vous pouvez simplement vérifier une réponse et s'il n'y a pas de réponse, vous appelez à nouveau la fonction. En optimisant votre code, je pense qu'il devrait ressembler à quelque chose comme ceci

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


7 commentaires

@ sven-hig: Merci pour vos commentaires. Le fait est que je dois appeler un certain nombre de fois api. c'est à dire; si le client donne une entrée à 3, le nombre maximal de tentatives de relance sera de 3, si le résultat est nul. C'est pourquoi j'ai utilisé setTimeout. S'il vous plait, n'hésitez pas à commenter.


J'ai mis à jour le code pour en tenir compte, essayez-le et voyez si cela fonctionne pour vous


@ sven-hug Avons-nous besoin d'autre cas lorsque la réponse est nulle? Parce que nous vérifions déjà dans le code si la réponse est là ou non. Corrigez-moi si je me trompe.


si vous entendez par réponse est nul un objet vide, non vous n'êtes pas obligé car c'est considéré comme une réponse réussie, vous pouvez probablement afficher un message au client "Aucune donnée disponible", j'ai modifié un peu le code et testé ainsi cela devrait fonctionner, vérifiez-le et faites-moi savoir si cela fonctionne pour vous, si vous avez d'autres questions, n'hésitez pas à poser


@ sven-hig Oui, je voulais dire à la fois si (réponse) et else return response font la même chose, n'est-ce pas? c'est donc redondant.


la première réponse est lorsque vous effectuez le premier appel de l'API, puis s'il échoue, il se met en boucle et cela a 2 résultats, soit vous obtenez une réponse, puis votre résolution que c'est là que l'autre vient jouer ou vous montrez un msg "Aucune donnée ....."


En fait, vous avez raison, j'ai à nouveau regardé le code, il n'y a pas besoin de else



0
votes

Au point où return setTimeOut () apparaît, vous devez renvoyer une Promise ..... que setTimeOut () ne fournit pas.

Le talent est de savoir comment "promisifier" setTimeOut()

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

Avec delay () en place, vous peut écrire:

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


0 commentaires