Concurrencia en Javascript

Piero Divasto

 

  • Software Project Lead @ MercadoLibre
     
  • M.Sc. in Business Informatics
    Universidad de Mannheim

     
  • Ingeniero civil Informático
    Universidad de Valparaíso

Concurrencia !== Paralelismo

T1

T2

T3

T1

T2

T3

Tiempo

CPU 1

CPU 2

T1

T2

T3

T1

T2

T3

Tiempo

CPU 1

T1

T2

T3

T1

T2

T3

JavaScript is a prototype-based, multi-paradigm, dynamic language, single-threaded, supporting object-oriented, imperative, and declarative (e.g. functional programming) styles.

https://developer.mozilla.org/en-US/docs/Web/JavaScript
https://www.youtube.com/watch?v=CuwGYz2WgFs&t=1980s

El modelo de concurrencia de JavaScript es como tener un restaurant el cual tiene una cantidad infinita de mesas y que puedes sentar la cantidad de personas que quieras, pero que tiene una sola cocina.

Event-Loop

stack

task queue

Web Api

heap

Event-Loop

stack

Web Api

Event-Loop

console

code

console.log("Hola!")

setTimeout(function() {
  console.log("La Previa 2023!");
}, 3000)

console.log("Saludos!");

task queue

stack

Web Api

Event-Loop

console

code

console.log("Hola!") <-----

setTimeout(function() {
  console.log("Codeton 2020!");
})

console.log("Saludos!");

main()

console.log("Hola!");

console.log("Hola!") <----

setTimeout(function() {
  console.log("La Previa 2023!");
}, 3000)

console.log("Saludos!");

task queue

stack

Web Api

Event-Loop

console

code

console.log("Hola!") <-----

setTimeout(function() {
  console.log("La Previa 2023!");
})

console.log("Saludos!");

console.log("Hola!");

task queue

stack

Web Api

Event-Loop

console

code

console.log("Hola!")

setTimeout(function() { <-----
  console.log("La Previa 2023!");
}, 3000);

console.log("Saludos!");

> "Hola!"

setTimeout(...)

task queue

stack

Web Api

Event-Loop

console

code

console.log("Hola!")

setTimeout(function() { 
  console.log("La Previa 2023!");
}, 3000);

console.log("Saludos!");

> "Hola!"

timeout(3 secs)

task queue

stack

Web Api

Event-Loop

console

code

console.log("Hola!")

setTimeout(function() { 
  console.log("La Previa 2023!");
});

console.log("Saludos!"); <----

> "Hola!"

timeout(3 secs)

console.log("Sal...");

task queue

stack

Web Api

Event-Loop

console

code

console.log("Hola!")

setTimeout(function() { 
  console.log("La Previa 2023!");
});

console.log("Saludos!");

> "Hola!"

timeout(3 secs)

> "Saludos!"

task queue

stack

Web Api

Event-Loop

console

code

console.log("Hola!")

setTimeout(function() { 
  console.log("La Previa 2023!");
});

console.log("Saludos!");

> "Hola!"

timeout()

> "Saludos!"

task queue

stack

Web Api

Event-Loop

console

code

console.log("Hola!")

setTimeout(function() { 
  console.log("La Previa 2023!");
}, 3000);

console.log("Saludos!");

> "Hola!"

> "Saludos!"

task queue

timeout()

stack

Web Api

Event-Loop

console

code

console.log("Hola!")

setTimeout(function() { 
  console.log("La Previa 2023!");
}, 3000);

console.log("Saludos!");

> "Hola!"

> "Saludos!"

> "Codeton 2020!"

task queue

Como manejamos la concurrencia en nuestro código?

Callbacks

Promises

Generators

async/await

Como manejamos la concurrencia en nuestro código?

Callbacks

function greeting(name) {
  alert('Hello ' + name);
}

function processUserInput(callback) {
  var name = prompt('Please enter your name.');
  callback(name);
}

processUserInput(greeting);

Callbacks

// Ir a un local y comprar un completo

irAlLocal(direccion, function hacerPedido(err) {
  if (err) {
    console.error("Algo sucedió cuando iba al local");
    return;
  } else {
    pedirCompleto(ingredientes, function notificarPedidoListo(err) {
      if (err) {
        console.error("Hubo un problema con su completo");
        return;
      } else {
        llamarCliente("1234", function buscarPedido(err) {
      		if(err) {
              console.error("Algo ocurrio cuando llamamos al cliente.");
              return;
            } else {
              recogerPedido();
              caminarMesa();
              ...
            }
        });
      }
    });
  } 
});

Callbacks

Problemas

Inversion-of-control

Callback hell

Difícil de entender

Promises

new Promise(function(resolve, reject) {
  if (good) {
    resolve(value);
  } else {
    reject(value);
  }
}); 

Promises

Chaining

const promise = hacerAlgo();
const promise2 = promise.then(successCallback, failureCallback);

// Equivalente a

const promise2 = hacerAlgo().then(successCallback, failureCallback);

Promises

Chaining

hacerAlgo()
  .then(function(resultado) {
    return hacerAlgoMas(resultado);
  })
  .then(function(nuevoResultado) {
    return hacerUnaTerceraCosa(nuevoResultado);
  })
  .then(function(resultadoFinal) {
    console.log('Resultado final es: ' + resultadoFinal);
  })
  .catch(failureCallback);

Promises

Error handling

hacerAlgo()
  .then(function(resultado) {
    return hacerAlgoMas(resultado);
  }, function manejarErrorAnterior() {
    console.error("Algo sucedio en la promesa anterior");
  })
  .then(function(nuevoResultado) {
    return hacerUnaTerceraCosa(nuevoResultado);
  }, function manejarErrorAnterior() {
    console.error("Algo sucedio en la promesa anterior");
  })
  .then(function(resultadoFinal) {
    console.log('Resultado final es: ' + resultadoFinal);
  }, function manejarErrorAnterior() {
    console.error("Algo sucedio en la promesa anterior");
  })
  .catch(failureCallback);

Promises

irAlLocal(direccion)
  .then(function() {
    return hacerPedido();
  })
  .then(function() {
    const ingredientes = [...]; 
    return pedirCompleto(ingredientes);
  })
  .then(function() {
    return notificarPedidoListo();
  })
  .then(function() {
    return llamarCliente();
  })
  .then(function() {
    recogerPedido();
    caminarMesa();
    ...
  });

Promises

irAlLocal(direccion)
  .then(hacerPedido)
  .then(function() {
    const ingredientes = [...]; 
    return pedirCompleto(ingredientes);
  })
  .then(notificarPedidoListo)
  .then(llamarCliente)
  .then(function() {
    recogerPedido();
    caminarMesa();
    ...
  });

Promises

https://developer.mozilla.org/es/docs/Web/JavaScript/Referencia/Objetos_globales/Promise

Promises

Composición

Promise.all([funcion1(), funcion2(), funcion3()])
  .then(function ([resultado1, resultado2, resultado3]) { 
  	/* Usar resultado1, resultado2 and resultado3 */ 
  });

//------------

Promise.race([funcion1(), funcion2(), funcion3()])
  .then(function (resultado) { 
  	/* Usar resultado */
  	/* En este caso, resultado será el resultado de
  	 * la promesa que se resuelva primero */
  });

Generators

function* generadorNumero(desde = 0, hasta = 100, step = 1) {
    let contadorIteracion = 0;
    for (let i = desde; i < hasta; i += step) {
        contadorIteracion++;
        yield i;
    }
    return contadorIteracion;
}

let gen = generadorNumero();

console.log(gen); // Object [Generator] {} (Iterator)

console.log(gen.next()); // { value: 0, done: false }
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
// ...
console.log(gen.next()); // { value: 100, done: true }
console.log(gen.next()); // { value: undefined, done: true }

Async/await

async function laPrevia() {
  try{
    const resultado = await hacerAlgo();
    const nuevoResultado = await hacerAlgoMas(resultado);
    const resultadoFinal = await hacerUnaTerceraCosa(nuevoResultado);
    
    console.log('Resultado final es: ' + resultadoFinal);
  } catch(error) {
    failureCallback(error);
  }
}
hacerAlgo()
  .then(function(resultado) {
    return hacerAlgoMas(resultado);
  })
  .then(function(nuevoResultado) {
    return hacerUnaTerceraCosa(nuevoResultado);
  })
  .then(function(resultadoFinal) {
    console.log('Resultado final es: ' + resultadoFinal);
  })
  .catch(failureCallback);

Async/await

async function comerCompletos (donde) {
  const ingredientes = [...];
  try {
    await irAlLocal(donde);
    await pedirCompleto(ingredientes);
    await notificarPedidoListo();
    await llamarCliente();
    recogerPedido();
    caminarMesa();
  } catch (error) {
    console.error(error);
  }
}

Async/await + Promises

async function laPrevia() {
  try{
    const resultado = await hacerAlgo();
    const nuevoResultado = await hacerAlgoMas(resultado); // Independiente
    const segundoNuevoResultado = await hacerOtraCosa(resultado); // Independiente
    const resultadoFinal = await hacerUnaTerceraCosa(nuevoResultado, segundoNuevoResultado);
    
    console.log('Resultado final es: ' + resultadoFinal);
  } catch(error) {
    failureCallback(error);
  }
}
async function laPrevia() {
  try{
    const resultado = await hacerAlgo();
    const [nuevoResultado, segundoNuevoResultado] = 
          await Promise.all([hacerAlgoMas(resultado), hacerOtraCosa(resultado)];
    const resultadoFinal = await hacerUnaTerceraCosa(nuevoResultado, segundoNuevoResultado);
    
    console.log('Resultado final es: ' + resultadoFinal);
  } catch(error) {
    failureCallback(error);
  }
}

http://github.com/morph3o

https://medium.com/@pierodivasto

https://www.linkedin.com/in/pierodivasto

https://slides.com/morph3o

Gracias!

Feedback! 🙏 

https://bit.ly/37tnGFy

Recursos

Videos:

  • https://www.youtube.com/watch?v=8aGhZQkoFbQ
  • https://www.youtube.com/watch?v=CuwGYz2WgFs
  • https://www.youtube.com/watch?v=Qg1SvpIau6U

Libros:

  • You Don't Know JS: Async & Performance
    • https://github.com/getify/You-Dont-Know-JS

Concurrencia en Javascript

By Piero Divasto

Concurrencia en Javascript

Charla sobre concurrencia en Javascript para la conferencia codeton

  • 728