Suspender en JavaScript: retraso entre acciones

129

¿Hay alguna manera de dormir en JavaScript antes de que realice otra acción?

Ejemplo:

 var a = 1+3;
 // Sleep 3 seconds before the next action here
 var b = a + 4;
Jin Yong
fuente

Respuestas:

140

Puede usar setTimeoutpara lograr un efecto similar:

var a = 1 + 3;
var b;
setTimeout(function() {
    b = a + 4;
}, (3 * 1000));

Esto realmente no 'duerme' JavaScript, solo ejecuta la función pasada setTimeoutdespués de una cierta duración (especificada en milisegundos). Aunque es posible escribir una función de suspensión para JavaScript, es mejor usarlo setTimeoutsi es posible, ya que no congela todo durante el período de suspensión.

Steve Harrison
fuente
9
También eche un vistazo a setInterval (). Es similar a setTimeout (), pero su función se llama varias veces (hasta que la detiene), lo cual es útil si desea hacer algo mientras duerme (como hacer actualizaciones de progreso, mantener algún estado interno o lo que sea).
Anders Sandvig
55
Esto no responde la pregunta. La pregunta pide un "sueño" equivalente que no es.
Felwithe
Aunque esta respuesta no coincide con la pregunta, es más útil que el bucle y compara Date.now (). Nadie debe usar un bucle bloqueado para implementar el sueño.
Li Chunlin
2
A menos, por supuesto, que un bucle de bloqueo sea exactamente lo que alguien quiere.
Wonko el cuerdo
55

En caso de que realmente necesites un sleep()solo para probar algo. Pero tenga en cuenta que bloqueará el navegador la mayoría de las veces durante la depuración, probablemente es por eso que lo necesita de todos modos. En modo producción comentaré esta función.

function pauseBrowser(millis) {
    var date = Date.now();
    var curDate = null;
    do {
        curDate = Date.now();
    } while (curDate-date < millis);
}

No lo use new Date()en el bucle, a menos que quiera desperdiciar memoria, potencia de procesamiento, batería y posiblemente la vida útil de su dispositivo.

Rodrigo
fuente
8
Esta respuesta merece más votos. Protagonizar la pregunta solo por esta respuesta.
jagc
¿Qué pasa con la advertencia de "demasiada recursividad"?
Oki Erie Rinaldi
1
@OkiErieRinaldi No hay recurrencia allí, es solo un bucle.
Rodrigo
77
@ 3.1415926535897932384626433833 Bueno, alguien pidió una función de "suspensión", eso es lo que está aquí. Lo usé una vez, no recuerdo exactamente para qué tipo de depuración. Si alguna vez lo necesito nuevamente, sé exactamente dónde encontrarlo. Si prefiere otra función, es su elección. ¿No es genial poder elegir?
Rodrigo
2
"Ocupado esperando".
Zeek2
13

Versión ECMAScript 6, utilizando generadores con rendimiento para "bloqueo de código":

Debido a que la pregunta original se publicó hace siete años, no me molesté en responder con el código exacto, porque es demasiado fácil y ya está respondida. Esto debería ayudar en problemas más complicados, como si necesita al menos dos durmientes, o si planea secuenciar una ejecución asincrónica. Siéntase libre de modificarlo para satisfacer sus necesidades.

let sleeptime = 100
function* clock()
{
    let i = 0
    while( i <= 10000 )
    {
        i++
        console.log(i); // actually, just do stuff you wanna do.
        setTimeout(
            ()=>
            {
                clk.next()
            }
            , sleeptime
        )
        yield
    }
}

let clk = clock()
clk.next()

función*

() => función de flecha

También puede encadenar eventos a través de Promesas :

function sleep(ms)
{
    return(
        new Promise(function(resolve, reject)
        {
            setTimeout(function() { resolve(); }, ms);
        })
    );
}


sleep(1000).then(function()
{
    console.log('1')
    sleep(1000).then(function()
    {
        console.log('2')
    })
})

O sería mucho más simple y menos elegante

function sleep(ms, f)
{
    return(
        setTimeout(f, ms)
    )
}


sleep(500, function()
{
    console.log('1')
    sleep(500, function()
    {
        console.log('2')
    })
})
console.log('Event chain launched')

Si solo está esperando que ocurra alguna condición, puede esperar así

function waitTill(condition, thenDo)
{
    if (eval(condition))
    {
        thenDo()
        return
    }

    setTimeout(
        ()    =>
        {
            waitTill(condition, thenDo)
        }
        ,
        1
    )
}

x=0

waitTill(
    'x>2 || x==1'
    ,
    ()    =>
    {
        console.log("Conditions met!")
    }
)

// Simulating the change
setTimeout(
    () =>
    {
        x = 1
    }
    ,
    1000
)

Íhor Mé
fuente
11

Actualización 2018

Los últimos Safari, Firefox y Node.js ahora también son compatibles con async / await / promises.

Usando async / await / Promises:

(A partir del 1/2017, compatible con Chrome, pero no con Safari, Internet Explorer, Firefox, Node.js)

'use strict';

function sleep(ms) {
  return new Promise(res => setTimeout(res, ms));
}

let myAsyncFunc = async function() {
  console.log('Sleeping');
  await sleep(3000);
  console.log('Done');
}

myAsyncFunc();

Actualización 2017

JavaScript ha evolucionado desde que se hizo esta pregunta y ahora tiene funciones de generador, y se está implementando el nuevo async / await / Promise. A continuación hay dos soluciones, una con función de generador que funcionará en todos los navegadores modernos, y otra, utilizando el nuevo async / wait que aún no es compatible en todas partes.

Usando una función de generador:

'use strict';

let myAsync = (g) => (...args) => {
    let f, res = () => f.next(),
        sleep = (ms) => setTimeout(res, ms);
    f = g.apply({sleep}, args); f.next();
};

let myAsyncFunc = myAsync(function*() {
    let {sleep} = this;
    console.log("Sleeping");
    yield sleep(3000);
    console.log("Done");
});

myAsyncFunc();

Preste atención al hecho de que ambas soluciones son de naturaleza asíncrona. Esto significa que myAsyncFunc (en ambos casos) volverá mientras duerme.

Es importante tener en cuenta que esta pregunta es diferente a ¿Cuál es la versión JavaScript de sleep ()? donde el solicitante solicita un sueño real (ninguna otra ejecución de código en el proceso) en lugar de un retraso entre las acciones.

niry
fuente
1
¡La mejor respuesta hasta ahora! He pasado 30 minutos buscando por todas partes para encontrar eso ... ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡una gran gracias !!!
538ROMEO
1
Perdí esta respuesta mientras buscaba una solución y reinventé la bicicleta: D ¡¡Ojalá la viera antes y me ahorraría horas! ¡Votado!
sserzant
let co = gen => (...args) => { let iter = gen(...args); let resume = () => new Promise((resolve, reject) => { let result = iter.next(); if (result.done) resolve(result.value); else Promise.resolve(result.value).then(resume).then(resolve, reject); }); return resume(); };le permitiría hacerlo let asyncAdd = co(function* (a, b) { console.log('Sleeping'); yield sleep(3000); console.log('Done'); return a + b; }); asyncAdd(3, 4).then(console.log);utilizando la definición de sleep()su segundo bloque de código.
Patrick Roberts
3

Si desea funciones menos torpes que setTimeouty setInterval, puede envolverlas en funciones que simplemente invierten el orden de los argumentos y les dan nombres agradables:

function after(ms, fn){ setTimeout(fn, ms); }
function every(ms, fn){ setInterval(fn, ms); }

Versiones de CoffeeScript:

after = (ms, fn)-> setTimeout fn, ms
every = (ms, fn)-> setInterval fn, ms

Luego puede usarlos bien con funciones anónimas:

after(1000, function(){
    console.log("it's been a second");
    after(1000, function(){
        console.log("it's been another second");
    });
});

Ahora se lee fácilmente como "después de N milisegundos, ..." (o "cada N milisegundos, ...")

1j01
fuente
2

Otra forma de hacerlo es mediante Promise y setTimeout (tenga en cuenta que debe estar dentro de una función y configurarla como asíncrona con la palabra clave asíncrona):

async yourAsynchronousFunction () {

    var a = 1+3;

    await new Promise( (resolve) => {
        setTimeout( () => { resolve(); }, 3000);
    }

    var b = a + 4;

}
Ostn
fuente
2

Aquí hay una manera muy simple de hacerlo que 'se siente' como una pausa / suspensión síncrona, pero es un código asíncrono legítimo.

// Create a simple pause function
const pause = (timeoutMsec) => new Promise(resolve => setTimeout(resolve,timeoutMsec))

async function main () {
    console.log('starting');
    // Call with await to pause.  Note that the main function is declared asyc
    await pause(3*1000)
    console.log('done');
}

usuario2330237
fuente
1

Puede usar JavaScript simple, esto llamará a su función / método después de 5 segundos:

setTimeout(()=> { your_function(); }, 5000);
El caballo negro
fuente
0

Hay varias formas de resolver este problema. Si usamos la setTimeoutfunción, vamos a conocerla primero. Esta función tiene tres parámetros: functiono code, delay(en milisegundos) y el parameters. Como se requiere la función o el parámetro de código , los otros son opcionales. Una vez que no haya ingresado el retraso , se establecerá en cero.

Para más detalles sobre el setTimeout() ir a este enlace .

Versión simplificada:

var a = 1 + 3;
var b;
console.log('a = ' + a);
setTimeout(function(){ 
    b = a + 4; 
    console.log('b = ' + b);
}, 1000);

salida:
a = 4
24 -> Identificador de número de la lista de tiempos de espera activos
b = 8


Usando el paso de parámetro:

var a = 1 + 3;
var b;
console.log('a = ' + a);
setTimeout(myFunction, 1000, a);

function myFunction(a)
{
    var b = a + 4;
    console.log('b = ' + b);
}

salida:
a = 4
25 -> Identificador de número de la lista de tiempos de espera activos
b = 8



Soporte de navegador:

Chrome Firefox Edge Safari Opera
1.0 1.0 4.0 1.0 4.0
Alexandre Neukirchen
fuente
0

Este es mi modelo que muestra cómo "dormir" o "DoEvents" en JavaScript utilizando una función de generador (ES6). Código comentado:

<html>
<head>
<script>
  "use strict"; // always
  // Based on post by www-0av-Com /programming/3143928
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
  var divelt, time0, globaln = 0; // global variables
  var MainGenObj = Main(); // generator object = generator function()
window.onload = function() {
  divelt = document.getElementsByTagName("body")[0]; // for addline()
  addline("typeof Main: " + typeof Main);
  addline("typeof MainDriver: " + typeof MainDriver);
  addline("typeof MainGenObj: " + typeof MainGenObj);
  time0 = new Date().valueOf(); // starting time ms
  MainDriver(); // do all parts of Main()
}
function* Main() { // this is "Main" -- generator function -- code goes here
  // could be loops, or inline, like this:

  addline("Part A, time: " + time() + ", " + ++globaln); // part A
  yield 2000;                    // yield for 2000 ms (like sleep)

  addline("Part B, time: " + time() + ", " +  ++globaln); // part B
  yield 3000;                    // yield for 3000 ms (or like DoEvents)

  addline("Part Z, time: " + time() + ", " +  ++globaln); // part Z (last part)
  addline("End, time: " + time());
}
function MainDriver() { // this does all parts, with delays
  var obj = MainGenObj.next(); // executes the next (or first) part of Main()
  if (obj.done == false) { // if "yield"ed, this will be false
    setTimeout(MainDriver, obj.value); // repeat after delay
  }
}
function time() { // seconds from time0 to 3 decimal places
  var ret = ((new Date().valueOf() - time0)/1000).toString();
  if (ret.indexOf(".") == -1) ret += ".000";
  while (ret.indexOf(".") >= ret.length-3) ret += "0";
  return ret;
}
function addline(what) { // output
  divelt.innerHTML += "<br />\n" + what;
}
</script>
</head>
<body>
<button onclick="alert('I\'m alive!');"> Hit me to see if I'm alive </button>
</body>
</html>
dcromley
fuente
0

Prueba esta función:

const delay = (ms, cb) => setTimeout(cb, ms)

Así es como lo usas:

console.log("Waiting for 5 seconds.")
delay(5000, function() {
  console.log("Finished waiting for 5 seconds.")
})

O ve a prometer estilo:

const delay = ms => new Promise(resolve => {
    setTimeout(resolve, ms)
})

Aquí hay una demostración .

Richie Bendall
fuente