¿Forma correcta de esperar a que finalice una función antes de continuar?

Resuelto DA. asked hace 10 años • 0 respuestas

Tengo dos funciones JS. Uno llama al otro. Dentro de la función de llamada, me gustaría llamar a la otra, esperar a que finalice esa función y luego continuar. Entonces, por ejemplo/pseudocódigo:

function firstFunction(){
    for(i=0;i<x;i++){
        // do something
    }
};

function secondFunction(){
    firstFunction()
    // now wait for firstFunction to finish...
    // do something else
};

Se me ocurrió esta solución, pero no sé si es una forma inteligente de hacerlo.

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()
    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

¿Es eso legítimo? ¿Existe una forma más elegante de manejarlo? ¿Quizás con jQuery?

DA. avatar Feb 03 '14 08:02 DA.
Aceptado

Una forma de lidiar con trabajos asincrónicos como este es usar una función de devolución de llamada, por ejemplo:

function firstFunction(_callback){
    // do some asynchronous work
    // and when the asynchronous stuff is complete
    _callback();    
}

function secondFunction(){
    // call first function and pass in a callback function which
    // first function runs when it has completed
    firstFunction(function() {
        console.log('huzzah, I\'m done!');
    });    
}

Según la sugerencia de @Janaka Pushpakumara, ahora puedes usar funciones de flecha para lograr lo mismo. Por ejemplo:

firstFunction(() => console.log('huzzah, I\'m done!'))


Actualización: respondí esto hace bastante tiempo y realmente quiero actualizarlo. Si bien las devoluciones de llamada están absolutamente bien, según mi experiencia, tienden a generar un código que es más difícil de leer y mantener. Sin embargo, hay situaciones en las que todavía los uso, como para pasar eventos en progreso y similares como parámetros. Esta actualización es sólo para enfatizar las alternativas.

Además, la pregunta original no menciona específicamente async, por lo que en caso de que alguien esté confundido, si su función es sincrónica, se bloqueará cuando se llame. Por ejemplo:

doSomething()
// the function below will wait until doSomething completes if it is synchronous
doSomethingElse()

Aunque, como está implícito, la función es asincrónica, la forma en que tiendo a lidiar con todo mi trabajo asincrónico hoy es con async/await. Por ejemplo:

const secondFunction = async () => {
  const result = await firstFunction()
  // do something else here after firstFunction completes
}

En mi opinión, async/await hace que su código sea mucho más legible que usar promesas directamente (la mayor parte de las veces). Si necesita controlar los errores de captura, utilícelo con try/catch. Lea más sobre esto aquí: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function .

Matt Way avatar Feb 03 '2014 01:02 Matt Way

Utilice asíncrono/espera:

async function firstFunction(){
  for(i=0;i<x;i++){
    // do something
  }
  return;
};

luego use await en su otra función para esperar a que regrese:

async function secondFunction(){
  await firstFunction();
  // now wait for firstFunction to finish...
  // do something else
};
Fawaz avatar Aug 17 '2018 11:08 Fawaz

Una forma elegante de esperar a que se complete primero una función es usar Promesas con la función async/await .


  1. En primer lugar, cree una Promesa . La función que creé se completará después de 2 segundos. Lo usé setTimeoutpara demostrar la situación en la que las instrucciones tardarían algún tiempo en ejecutarse.
  2. Para la segunda función, puede usar async/await una función donde deberá awaitcompletar la primera función antes de continuar con las instrucciones.

Ejemplo:

    //1. Create a new function that returns a promise
    function firstFunction() {
      return new Promise((resolve, reject) => {
          let y = 0
          setTimeout(() => {
            for (i=0; i<10; i++) {
               y++
            }
             console.log('Loop completed.')  
             resolve(y)
          }, 2000)
      })
    }
    
    //2. Create an async function
    async function secondFunction() {
        console.log('Before promise call.')
        //3. Await for the first function to complete
        const result = await firstFunction()
        console.log('Promise resolved: ' + result)
        console.log('Next step.')
    }; 

    secondFunction()
Expandir fragmento


Nota:

Podrías simplemente resolvehacerlo Promisesin ningún valor así resolve(). En mi ejemplo, tengo resolvedel Promisevalor de ythat y luego puedo usarlo en la segunda función.

Jakub A Suplicki avatar Apr 16 '2019 00:04 Jakub A Suplicki

Aviso

Esta respuesta está desactualizada para los estándares actuales. async/awaitConsidere la posibilidad de utilizar promesas y sintaxis nativas de JavaScript .


Parece que te estás perdiendo un punto importante aquí: JavaScript es un entorno de ejecución de un solo subproceso. Miremos nuevamente su código, tenga en cuenta que agregué alert("Here"):

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()

    alert("Here");

    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

No tienes que esperar isPaused. Cuando veas la alerta "Aquí", isPausedya estarás false, y firstFunctionhabrás regresado. Esto se debe a que no puede "ceder" desde dentro del forbucle ( // do something), es posible que el bucle no se interrumpa y primero tendrá que completarse por completo (más detalles: manejo de subprocesos de Javascript y condiciones de carrera ).

Dicho esto, aún puedes hacer que el código fluya internamente firstFunctionpara que sea asíncrono y usar una devolución de llamada o una promesa para notificar a la persona que llama. Tendrías que renunciar al forbucle y simularlo con if( JSFiddle ):

function firstFunction()
{
    var deferred = $.Deferred();
    
    var i = 0;
    var nextStep = function() {
        if (i<10) {
            // Do something
            printOutput("Step: " + i);
            i++;
            setTimeout(nextStep, 500); 
        }
        else {
            deferred.resolve(i);
        }
    }
    nextStep();
    return deferred.promise();
}

function secondFunction()
{
    var promise = firstFunction();
    promise.then(function(result) { 
        printOutput("Result: " + result);
    });
}

Como nota al margen, JavaScript 1.7 ha introducido yieldpalabras clave como parte de los generadores . Eso permitirá "perforar" agujeros asincrónicos en un flujo de código JavaScript que de otro modo sería síncrono ( más detalles y un ejemplo ). Sin embargo, la compatibilidad del navegador con generadores está actualmente limitada a Firefox y Chrome, AFAIK.

noseratio avatar Feb 03 '2014 09:02 noseratio

Me pregunto por qué nadie ha mencionado este patrón simple. :

(function(next) {
  //do something
  next()
}(function() {
  //do some more
}))

Usar tiempos de espera sólo para esperar ciegamente es una mala práctica; e involucrar promesas solo agrega más complejidad al código. En el caso de OP:

(function(next) {
  for(i=0;i<x;i++){
    // do something
    if (i==x-1) next()
  }
}(function() {
  // now wait for firstFunction to finish...
  // do something else
}))

una pequeña demostración -> http://jsfiddle.net/5jdeb93r/

davidkonrad avatar Oct 11 '2020 16:10 davidkonrad