Tengo un punto final en mi servidor llamado "/order".
Cuando se activa, necesito enviar un pedido a una API. A veces, debido a que algunos datos a través de ipfs tardan demasiado en descargarse, el pedido falla dentro del bloque try-catch y necesito volver a enviarlo.
Dado que un bucle while que intenta reenviar la orden hasta que tenga éxito estaría bloqueando otras solicitudes, pensé que podría hacer uno yo mismo usando recursividad y setTimeout, para intentar enviar la misma solicitud, digamos, 3 veces cada 5 minutos y si la tercera vez que falla, entonces no lo intentará de nuevo.
Quería saber si esta era la forma correcta de lograr la funcionalidad sin bloqueo y si hay algunas vulnerabilidades/cosas que no estoy teniendo en cuenta:
async function makeOrder(body, tries) { if (tries > 0) { let order; try { order = getOrderTemplate(body.shipping_address) for (const index in body.line_items) { order.items.push(await getItemTemplate(body.line_items[index])) } await sendOrderToAPI(order) } catch (err) { setTimeout(function(){ makeOrder(body, tries - 1) }, 180000) } } else { console.log("order n " + body.order_number + " failed") //write failed order number to database to deal with it later on } }
Un problema importante es que, si hay un problema, devolverá una Promesa que no se resuelve cuando finaliza la llamada API final, sino cuando finaliza la llamada API inicial (fallida). Este:
setTimeout(function(){ makeOrder(body, tries - 1) }, 180000)
no está encadenado correctamente con la función async
externa.
Como resultado, la siguiente lógica fallará:
makeOrder(body, 3) .then(() => { // All orders are made })
En su lugar, prométalo, de modo que la llamada recursiva se pueda encadenar fácilmente con la función externa.
const delay = ms => new Promise(resolve => setTimeout(resolve, ms)); async function makeOrder(body, tries) { if (tries > 0) { let order; try { order = getOrderTemplate(body.shipping_address) for (const index in body.line_items) { order.items.push(await getItemTemplate(body.line_items[index])) } await sendOrderToAPI(order) } catch (err) { await delay(180_000); return makeOrder(body, tries - 1); } } else { console.log("order n " + body.order_number + " failed") //write failed order number to database to deal with it later on } }
Otra posible mejora sería, en lugar de await
dentro de un bucle aquí:
for (const index in body.line_items) { order.items.push(await getItemTemplate(body.line_items[index])) }
para usar Promise.all
, o un mecanismo diferente que permita obtener varias plantillas a la vez, en lugar de tener que esperar una por una en serie.
Otro problema potencial es que makeOrder
no rechaza cuando excede el número de intentos permitidos. Es decir, el consumidor no podría hacer:
makeOrder(body, 3) .catch((error) => { // implement logic here to handle the error })
Si desea permitir lo anterior, al final de makeOrder
, ejecute al mismo tiempo que está iniciando sesión:
} else { console.log("order n " + body.order_number + " failed") //write failed order number to database to deal with it later on throw new Error('Failed'); }