¿Cómo crear una función asíncrona en Javascript?

JavaScript es un lenguaje sincrónico y de un solo subproceso. El código se ejecuta en orden uno a la vez. Pero Javascript puede parecer asíncrono en algunas situaciones.

Ejemplo: 

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <div id="message"></div>
    <script>
        var msg = document.getElementById("message");
   
        function f1() {
            setTimeout(function () {
               msg.innerHTML += "
<p>f1 is starting</p>
";
               msg.innerHTML += "
<p>f1 is ending</p>
";
            }, 100);
        }
        function f2() {
            msg.innerHTML += "
<p>f2 is starting</p>
";
            f1();
            msg.innerHTML += "
<p>f2 is ending</p>
";
        }
   
        f2();
   
    </script>
</body>
</html>

Producción 

f2 is starting
f2 is ending
f1 is starting
f1 is ending

Ahora, podemos ver que después de ejecutar setTimeout(f1, 100), nuestro programa no está esperando que el temporizador termine, sino que salta a la siguiente declaración inmediatamente. Esto sucede porque si queremos ejecutar algún evento, JavaScript pone el evento en la cola de eventos y continúa con la ejecución normal del programa. El motor busca periódicamente en la cola de eventos para ver si es necesario llamar a algún evento o no.
Pero es posible que deseemos que nuestro programa espere hasta que se complete algún evento o trabajo en particular antes de procesar más comandos. 

Una función asíncrona se implementa mediante async, await y promises. 

  • async: la palabra clave «async» define una función asíncrona. 
Syntax
async function FunctionName(){
    ...
}
  • await: la función «async» contiene «await» que detiene la ejecución de la función «async». «await» solo es válido dentro de la función «async».
  • Promesa: Una Promesa es un valor proxy. Nos informa sobre el éxito/fracaso del evento asíncrono. Una Promesa debe contener una llamada resolve() o rechazó() o, de lo contrario, el consumidor de la Promesa nunca sabrá si la Promesa se cumple o no. Si eso sucediera, el programa seguirá esperando y ese bloque de código nunca más se ejecutará. Hay mucho más que prometer, pero podemos hacer que funcione asíncronamente sin un conocimiento profundo de ello.

Ejemplo: rehagamos el ejemplo anterior usando la función asíncrona.  

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <div id="message"></div>
    <script>
        var msg = document.getElementById("message");
   
        function f1() {
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                msg.innerHTML += "
<p>f1 is starting</p>
";
                    msg.innerHTML += "
<p>f1 is ending</p>
";
                    resolve();
                }, 100);
            })
        }
   
        async function f2() {
            msg.innerHTML += "
<p>f2 is starting</p>
";
               
            // Engine waits for f1() to finish it's 
            // execution before executing the next line
            await f1(); 
            msg.innerHTML += "
<p>f2 is ending</p>
";
        }
   
        f2();
   
    </script>
</body>
</html>

Producción: 

f2 is starting
f1 is starting
f1 is ending
f2 is ending

En el ejemplo anterior, el programa espera a que f1() complete su ejecución antes de continuar. La «espera» detiene la ejecución de ese segmento de código hasta que se recibe una Promesa. El resolve() se usa para resolver la Promesa. Significa que la Promesa se cumple. De manera similar a resolver, también podemos usar rechazar() para saber que la Promesa es rechazada. La función de rechazo() se usa principalmente para la depuración y el error, no necesitamos profundizar en ella por ahora.

Ejemplo: si queremos que la Promesa devuelva algún valor, podemos pasarlo en resolve(variable).  

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <div id="message"></div>
    <script>
        var msg = document.getElementById("message");
   
        function f1() {
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                   msg.innerHTML += "
<p>f1 is starting</p>
";
                    msg.innerHTML += "
<p>f1 is ending</p>
";
                    resolve(1);
                }, 100);
            })
        }
   
        async function f2() {
            msg.innerHTML += "
<p>f2 is starting</p>
";
            var p = await f1();
            if (p == 1) msg.innerHTML += "
<p>Promise Received</p>
"
            msg.innerHTML += "
<p>f2 is ending</p>
";
        }
   
        f2();
   
    </script>
</body>
</html>

Producción: 

f2 is starting
f1 is starting
f1 is ending
Promise Received
f2 is ending

Esperando por Múltiples Promesas: ¿Qué pasaría si tuviéramos que esperar por múltiples funciones? Tenemos dos formas de hacerlo.

Ejemplo: podemos escribir varias declaraciones de espera secuencialmente. 

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <div id="message"></div>
    <script>
        var msg = document.getElementById("message");
   
        function f1() {
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                   msg.innerHTML += "
<p>f1 is starting</p>
";
                   msg.innerHTML += "
<p>f1 is ending</p>
";
                   resolve();
                }, 1000);
            })
        }
   
        function f3() {
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                   msg.innerHTML += "
<p>f3 is starting</p>
";
                   msg.innerHTML += "
<p>f3 is ending</p>
";
                   resolve();
                }, 1000);
            })
        }
   
        async function f2() {
            msg.innerHTML += "
<p>f2 is starting</p>
";
            await f1();
            await f3();
            msg.innerHTML += "
<p>f2 is ending</p>
";
        }
   
        f2();
   
    </script>
</body>
</html>

Producción 

f2 is starting
f1 is starting
f1 is ending
f3 is starting
f3 is ending
f2 is ending
  • En el ejemplo anterior, primero obtenemos 
f2 is starting 
  • Luego, después de 1 segundo obtenemos 
f1 is starting
f1 is ending
  • Luego, después de otro 1 segundo, obtenemos 
f3 is starting
f3 is ending
f2 is ending

Ejemplo: La segunda forma de esperar varias Promesas es ejecutar las Promesas en paralelo usando Promise.all (objeto iterable). 

Sintaxis:  

await Promise.all(iterable object);

Ejemplo: 

HTML

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <div id="message"></div>
    <script>
        var msg = document.getElementById("message");
   
        function f1() {
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                   msg.innerHTML += "
<p>f1 is starting</p>
";
                   msg.innerHTML += "
<p>f1 is ending</p>
";
                   resolve();
                }, 1000);
            })
        }
   
        function f3() {
            return new Promise(function (resolve, reject) {
                setTimeout(function () {
                   msg.innerHTML += "
<p>f3 is starting</p>
";
                   msg.innerHTML += "
<p>f3 is ending</p>
";
                   resolve();
                }, 1000);
            })
        }
   
        async function f2() {
            msg.innerHTML += "
<p>f2 is starting</p>
";
            await Promise.all([f1(), f3()]);
            msg.innerHTML += "
<p>f2 is ending</p>
";
        }
   
        f2();
   
    </script>
</body>
</html>

Producción 

f2 is starting
f1 is starting
f1 is ending
f3 is starting
f3 is ending
f2 is ending
  • La salida es la misma que el código anterior, pero en este caso, el programa generará 
f2 is starting
  • Luego espere 1 segundo y salga 
f1 is starting
f1 is ending
f3 is starting
f3 is ending
f2 is ending

Dado que f1() y f3() se ejecutan en paralelo, no necesitamos esperar otro segundo antes de ejecutar f3(). En palabras simples, el temporizador de setTimeout() en f1() y f3() comienza al mismo tiempo.

Nota: También podemos implementar un comportamiento asíncrono usando solo Promises sin async/await y callbacks, consulte el siguiente enlace para eso: 

Publicación traducida automáticamente

Artículo escrito por Archaic y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *