El conocimiento es el nuevo dinero.
Aprender es la nueva manera en la que inviertes
Acceso Cursos

Dominar las promesas en programación: 8 consejos y técnicas avanzadas

🌟 Vamos a compartir 8 consejos avanzados que pueden elevar tu comprensión y uso de promesas. Estos consejos no sólo son perspicaces, también pueden mejorar en gran medida tu competencia. ¡Vamos a bucear en! 🚀

· 9 min de lectura
Dominar las promesas en programación: 8 consejos y técnicas avanzadas

🚀 En los proyectos JavaScript, aprovechar las promesas es crucial. Sorprendentemente, he observado que muchos desarrolladores frontend intermedios y experimentados, tanto entre colegas como entrevistadores, tienden a apegarse a prácticas convencionales como promiseInst.then(), promiseInst.catch(), y Promise.

Incluso con async/await, algunos lo utilizan sin una comprensión profunda de sus matices.

🧠 Sin embargo, las promesas ofrecen una plétora de casos de uso inteligentes y avanzados, algunos de los cuales se emplean ampliamente en la biblioteca de estrategias de petición Alova.

💡 Truco 1: Ejecución en serie de un array de promesas


Cuando nos enfrentamos a un escenario en el que una serie de tareas deben ejecutarse secuencialmente, el instinto inicial puede ser utilizar await. Sin embargo, un enfoque alternativo utilizando promesas puede ser más elegante.

const requestAry = [() => api.request1(), () => api.request2(), () => api.request3()];
// Using `await`
for (const requestItem of requestAry) {
 await requestItem();
}
// Using promises for serial execution
const finallyPromise = requestAry.reduce(
 (currentPromise, nextRequest) => currentPromise.then(() => nextRequest()),
 Promise.resolve() // Initial promise for linking promises in the array
);

Este método, que emplea la función then, permite una concatenación concisa y eficiente de promesas, asegurando la ejecución en serie de las tareas. 🚀

💡 Consejo 2: Cambio de estado fuera del ámbito de la nueva promesa


Imagina un escenario en el que varias páginas requieren la recopilación de información del usuario antes de permitir el uso de una función específica. Implementar esto puede ser abordado de manera diferente en función del nivel de habilidad de los desarrolladores frontend.

CPU
1 vCPU
MEMORIA
1 GB
ALMACENAMIENTO
10 GB
TRANSFERENCIA
1 TB
PRECIO
$ 4 mes
Para obtener el servidor GRATIS debes de escribir el cupon "LEIFER"

👶 FrontEnd elemental:
"Voy a crear una caja modal y copiar-pegar a través de las páginas. Super eficiente!"

👨‍💻 FrontEnd intermedio:
"Mantener esto es difícil. Encapsulemos el componente por separado e importémoslo donde sea necesario."

🚀FrontEnd avanzado:
"¡Por qué no encapsularlo todo! Escribir la llamada al método donde se pueda invocar en cualquier página?"

Para entender el enfoque avanzado, vamos a sumergirnos en un ejemplo de Vue 3:

<! - App.vue →
<template>
 <! - Modal box component →
 <div class="modal" v-show="visible">
 <div>
 User name: <input v-model="info.name" />
 </div>
 <! - Other information →
 <button @click="handleCancel">Cancel</button>
 <button @click="handleConfirm">Submit</button>
 </div>
 <! - Page components →
</template>
<script setup>
import { provide } from 'vue';
const visible = ref(false);
const info = reactive({
 name: ''
});
let resolveFn, rejectFn;
// Provide the information collection function
provide('getInfoByModal', () => {
 visible.value = true;
 return new Promise((resolve, reject) => {
 // Assign functions to the outside, breaking through the promise scope
 resolveFn = resolve;
 rejectFn = reject;
 });
})
const handleConfirm = () => {
 resolveFn && resolveFn(info);
};
const handleCancel = () => {
 rejectFn && rejectFn(new Error('User has canceled'));
};
</script>

Ahora, getInfoByModal puede obtener sin esfuerzo los datos del usuario llamando a la caja modal:

<template>
 <button @click="handleClick">Fill in the information</button>
</template>
<script setup>
import { inject } from 'vue';
const getInfoByModal = inject('getInfoByModal');
const handleClick = async () => {
 // After the call, the modal box will be displayed. After the user clicks to confirm, the promise fulfills, obtaining the user information.
 const info = await getInfoByModal();
 await api.submitInfo(info);
}
</script>

Esta técnica refleja la encapsulación de componentes comunes en varias bibliotecas de interfaz de usuario. 🌟

💡 Consejo 3: Desvelando el uso alternativo de Async/Await


Muchos están familiarizados con async/await como un medio para recibir el valor de retorno de una función async, sin embargo, pocos reconocen que una función async fundamentalmente devuelve una promesa. Considera estas dos funciones equivalentes:

const fn1 = async () => 1;
const fn2 = () => Promise.resolve(1);
fn1(); // Returns a promise object with a value of 1

Típicamente, await se utiliza con un objeto Promise, esperando a que se resuelva. En consecuencia, await para la función fn1 también es equivalente a lo siguiente:

await fn1();
const promiseInst = fn1();
await promiseInst;

Sin embargo, await guarda un secreto menos conocido. Cuando el valor seguido no es un objeto promesa, envuelve el valor en un objeto promesa. Por lo tanto, el código después de await debe ejecutarse de forma asíncrona. He aquí un ejemplo:

Promise.resolve().then(() => {
 console.log(1);
});
await 2;
console.log(2);
// Output order: 1 2

Esto equivale a:

Promise.resolve().then(() => {
 console.log(1);
});
Promise.resolve().then(() => {
 console.log(2);
});

¡Libera el poder del uso alternativo de await para un mayor control asíncrono! 🚀

💡 Consejo 4: Compromiso para implementar la compartición de peticiones


Enviar peticiones repetidas cuando una petición anterior aún está pendiente puede llevar a un consumo innecesario de recursos.

Una estrategia eficaz es compartir la respuesta de la petición inicial con las siguientes.

request('GET', '/test-api').then(response1 => {
 // …
});
request('GET', '/test-api').then(response2 => {
 // …
});

Sorprendentemente, las dos peticiones anteriores se ejecutan una sola vez, recibiendo ambas la misma respuesta de forma concurrente.

🌐 Escenarios de uso de la compartición de peticiones:

  1. Cuando se renderizan múltiples componentes internos en una página para obtener datos simultáneamente.
  2. Manejo de escenarios en los que el botón de envío no está desactivado, y el usuario hace clic en él varias veces sucesivamente.
  3. En casos de precarga de datos, navegar a la página de precarga antes de que la precarga se haya completado.

Esta función avanzada también forma parte de las capacidades de Alova. Para implementar la compartición de peticiones, aprovecha la función de caché de promesas, permitiendo que un objeto promise recupere datos a través de múltiples awaits. He aquí una idea de implementación sencilla:

const pendingPromises = {};
function request(type, url, data) {
 const requestKey = JSON.stringify([type, url, data]);
 if (pendingPromises[requestKey]) {
 return pendingPromises[requestKey];
 }
const fetchPromise = fetch(url, {
 method: type,
 data: JSON.stringify(data)
 })
 .then(response => response.json())
 .finally(() => {
 delete pendingPromises[requestKey];
 });
return pendingPromises[requestKey] = fetchPromise;
}

Al optimizar la compartición de peticiones, puedes gestionar los recursos de forma eficiente y mejorar la experiencia del usuario. 🚀

💡 Información adicional: Estado de promesa

Comprender las complejidades de los estados de promesa es crucial. Una vez que una promesa pendiente cambia de estado, permanece inmutable.

En el ejemplo dado, la promesa transita inicialmente al estado cumplido, y reject() posterior no la cambiará de nuevo al estado rechazado. Recuerda, el estado de una promesa se establece una vez que evoluciona. 🌈

💡 Consejo 6: Descifrar los valores de retorno then/catch/finally


Para desmitificar las complejidades, es esencial comprender que estas tres funciones devuelven cada una un nuevo objeto envuelto en una promesa.

El valor encapsulado corresponde al valor de retorno de la función callback. Si se lanza un error dentro de la llamada de retorno, se envuelve una promesa en un estado de rechazo. Vamos a desglosarlo con ejemplos:

🌀 Función then:

Promise.resolve().then(() => 1); // Returns new Promise(resolve => resolve(1))
Promise.resolve().then(() => Promise.resolve(2)); // Returns new Promise(resolve => resolve(Promise.resolve(2)))
Promise.resolve().then(() => { throw new Error('abc') }); // Returns new Promise(resolve => resolve(Promise.reject(new Error('abc'))))
Promise.reject().then(() => 1, () => 2); // Returns new Promise(resolve => resolve(2))

🌪 Función catch:

Promise.reject().catch(() => 3); // Returns new Promise(resolve => resolve(3))
Promise.resolve().catch(() => 4); // Returns new Promise(resolve => resolve(promise object calling catch))

🎯 Función finally:

Promise.resolve().finally(() => {}); // Returns Promise.resolve()
Promise.reject().finally(() => {}); // Returns Promise.reject()

Cuando el valor de retorno de la función finally es una promesa, espera a que la promesa se resuelva antes de devolver el objeto promesa antes de la función finally.

Promise.resolve(5).finally(() => new Promise(res => {
 setTimeout(res, 1000);
})); // Returns a pending Promise, resolves to 5 after 1 second.
Promise.reject(6).finally(() => new Promise(res => {
 setTimeout(res, 1000);
})); // Returns a pending Promise, rejects with 6 after 1 second.

La comprensión de estos valores de retorno desbloquea el potencial para el manejo preciso de promesas. 🚀

Consejo 7: Distinguir entre la segunda llamada de retorno de then y la llamada de retorno de catch Cuando se produce un error en una petición, se activan tanto la segunda función de callback como la  catchdethende una Promise.

A primera vista, pueden parecer similares, pero hay una diferencia crucial: la primera no puede capturar errores lanzados en la primera función callback dethen, mientras que catch sí puede.

Promise.resolve().then(
 () => {
 throw new Error('Error from success callback');
 },
 () => {
 // This block will not be executed
 }
).catch(reason => {
 console.log(reason.message); // Outputs: "Error from success callback"
});

Como se mencionó anteriormente, la función catch opera sobre la Promise rechazada devuelta por la función then, lo que le permite capturar errores de forma natural.

🛡️ Principio clave

La función catch es tu red de seguridad, capaz de capturar errores incluso desde la primera función callback.

💡 Tip 8 (Final)

Promise implementa el modelo de middleware de cebolla de Koa2
El framework Koa2 introduce el modelo cebolla, proporcionando un enfoque por capas para manejar las peticiones.

Las solicitudes atraviesan a través de capas como pelar una cebolla, asegurando un enfoque sistemático para el procesamiento previo y posterior a la solicitud.

🧅 Modelo de cebolla en Koa2

  • Las peticiones se mueven capa por capa a través de la pila de middleware.
  • Las capas procesan la petición y contribuyen a la respuesta.
  • Logra una estructura unificada para el manejo de peticiones.

Implementar este modelo con Promises asegura un enfoque robusto y organizado para el manejo de peticiones en tus aplicaciones. 🌐

Veamos un modelo simple de cebolla koa2:

const app = new Koa();
app.use(async (ctx, next) => {
  console.log('a-start');
  await next();
  console.log('a-end');
});
app.use(async (ctx, next) => {
  console.log('b-start');
  await next();
  console.log('b-end');
});

app.listen(3000);

La salida anterior es a-inicio -> b-inicio -> b-final -> a-final. ¿Cómo se consigue una secuencia de salida tan mágica? Algunas personas no tienen talento y simplemente lo implementan con unas 20 líneas de código.

Cualquier parecido es pura coincidencia.

A continuación analizamos

Nota: El siguiente contenido no es amigable para novatos, por favor lea con precaución.

Primero guarda la función middleware, y luego llama al modelo cebolla para su ejecución después de recibir la petición en la función listen.


function action(koaInstance, ctx) {
  // ...
}

class Koa {
   middlewares = [];
   use(mid) {
     this.middlewares.push(mid);
   }
   listen(port) {
     // Pseudocode simulates receiving request
     http.on('request', ctx => {
       action(this, ctx);
     });
   }
}

Tras recibir la solicitud, la lógica previa a la siguiente se ejecuta en serie empezando por el primer middleware.

//Start to start middleware call
function action(koaInstance, ctx) {
   let nextMiddlewareIndex = 1; // Identifies the next middleware index to be executed

   //Define next function
   function next() {
     // Before peeling the onion, calling next will call the next middleware function
     const nextMiddleware = middlewares[nextMiddlewareIndex];
     if (nextMiddleware) {
       nextMiddlewareIndex++;
       nextMiddleware(ctx, next);
     }
   }
   //Start execution from the first middleware function and pass in the ctx and next functions
   middlewares[0](ctx, next);
}

Procesar la lógica del puesto después del siguiente

function action(koaInstance, ctx) {
   let nextMiddlewareIndex = 1;
   function next() {
     const nextMiddleware = middlewares[nextMiddlewareIndex];
     if (nextMiddleware) {
       nextMiddlewareIndex++;
       // A return is also added here to allow the execution of the middleware function to be executed in series from back to front using promises (it is recommended to understand this return repeatedly)
       return Promise.resolve(nextMiddleware(ctx, next));
     } else {
       // When the pre-logic of the last middleware is executed, return the fullyfilled promise and start executing the post-logic after next.
       return Promise.resolve();
     }
   }
   middlewares[0](ctx, next);
}

En este punto, se ha aplicado un modelo simple de cebolla.

Conclusión


¡Embárcate en un viaje transformador para elevar tu experiencia en JavaScript con promesas! 🌟

Estos 8 consejos avanzados abarcan una serie de técnicas, desde la optimización de la ejecución secuencial de tareas hasta la revelación de potenciales ocultos en async/await.

Descubre formas elegantes, como cambiar el estado fuera del ámbito de la promesa y comprometerse a compartir peticiones de forma eficiente, para mejorar tu conjunto de herramientas de desarrollo.

🧠 Decodifique las complejidades de los valores de retorno en las funciones then, catch y finally, obteniendo un control preciso sobre la gestión de promesas.

Distinguir los matices entre la segunda devolución de llamada de then y la función catch para una gestión eficaz de los errores.

Por último, explore la implementación del modelo de cebolla Koa2 utilizando promesas, añadiendo un enfoque por capas a la gestión de peticiones.

¡Libera todo el potencial de las promesas para una experiencia de codificación transformadora y enriquecedora! ¡Feliz programación! 🚀

Fuente

Plataforma de cursos gratis sobre programación