Suspender em JavaScript - atraso entre ações

129

Existe uma maneira de eu dormir no JavaScript antes de executar outra ação?

Exemplo:

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

Respostas:

140

Você pode usar setTimeoutpara obter um efeito semelhante:

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

Isso realmente não 'adormece' o JavaScript - apenas executa a função transmitida setTimeoutapós uma certa duração (especificada em milissegundos). Embora seja possível escrever uma função de suspensão para JavaScript, é melhor usar setTimeoutse possível, pois não congela tudo durante o período de suspensão.

Steve Harrison
fonte
9
Veja também setInterval (). É semelhante ao setTimeout (), mas sua função é chamada várias vezes (até você pará-lo), o que é útil se você quiser fazer algo enquanto dorme (como fazer atualizações de progresso, manter algum estado interno ou qualquer outra coisa).
Anders Sandvig
5
Isso não responde à pergunta. A pergunta pede um equivalente "sono" que não seja.
23716 Feldithe
Embora essa resposta não corresponda à que a pergunta foi feita, é mais útil que o loop e compare Date.now (). Ninguém o que usar um loop bloqueado o implemento dorme.
Li Chunlin
2
A menos, é claro, que um loop de bloqueio seja exatamente o que alguém deseja.
Wonko the Sane
55

Caso você realmente precise de sleep()apenas um teste. Mas lembre-se de que ele travará o navegador na maioria das vezes durante a depuração - provavelmente é por isso que você precisa. No modo de produção, comentarei esta função.

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

Não use new Date()em loop, a menos que queira desperdiçar memória, energia de processamento, bateria e possivelmente a vida útil do seu dispositivo.

Rodrigo
fonte
8
Esta resposta merece mais votos. Estrelando a pergunta apenas causa desta resposta.
jagc
e quanto ao aviso de "muita recursão"?
Oki Erie Rinaldi
1
@OkiErieRinaldi Não há recursão lá, é apenas um loop.
911 Rodrigo
7
@ 3.1415926535897932384626433833 Bem, alguém pediu uma função "dormir", é isso que está aqui. Eu usei uma vez, não consigo lembrar exatamente para que tipo de depuração. Se eu precisar novamente, sei exatamente onde encontrá-lo. Se você preferir outra função, a escolha é sua. Não é ótimo poder escolher?
Rodrigo
2
"Ocupado esperando".
Zeek2
13

Versão ECMAScript 6, usando geradores com rendimento para "bloqueio de código":

Como a pergunta original foi publicada há sete anos, eu não me incomodei em responder com o código exato, porque é muito fácil e já foi respondido. Isso deve ajudar em problemas mais complicados, como se você precisar de pelo menos dois inativos ou se planeja sequenciar a execução assíncrona. Sinta-se livre para modificá-lo para atender às suas necessidades.

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()

função*

() => função da seta

Você também pode encadear eventos via Promises :

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')
    })
})

Ou muito mais simples e menos sofisticado seria

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')

Se você está apenas esperando que alguma condição aconteça, pode esperar assim

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é
fonte
11

Atualização de 2018

Os últimos Safari, Firefox e Node.js agora também oferecem suporte a async / waitit / promises.

Usando async / waitit / Promises:

(A partir de 1/2017, com suporte no Chrome, mas não no 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();

Atualização de 2017

O JavaScript evoluiu desde que essa pergunta foi feita e agora possui funções geradoras, e o novo async / waitit / Promise está sendo lançado. Abaixo, há duas soluções, uma com função de gerador que funcionará em todos os navegadores modernos e outra, usando o novo async / waitit que ainda não é suportado em todos os lugares.

Usando uma função de gerador:

'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 atenção ao fato de que essas duas soluções são de natureza assíncrona. Isso significa que o myAsyncFunc (em ambos os casos) retornará durante o sono.

É importante observar que esta pergunta é diferente de Qual é a versão JavaScript do sleep ()? onde o solicitante está solicitando uma suspensão real (nenhuma outra execução de código no processo), em vez de um atraso entre as ações.

niry
fonte
1
Melhor resposta até agora !! Passei 30 min procurando em todos os lugares para encontrar isso .. thx grande !!!
538ROMEO
1
Perdi a resposta enquanto procurava uma solução e reinventei a bicicleta: D Se ao menos eu a visse antes, me pouparia horas !! Voto a favor!
sserzant 23/01
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(); };permitiria que você let asyncAdd = co(function* (a, b) { console.log('Sleeping'); yield sleep(3000); console.log('Done'); return a + b; }); asyncAdd(3, 4).then(console.log);usasse a definição de sleep()do seu segundo bloco de código.
Patrick Roberts
3

Se você deseja menos funções desajeitadas que setTimeoute setInterval, pode envolvê-las em funções que apenas invertem a ordem dos argumentos e lhes dão nomes agradáveis:

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

Versões do CoffeeScript:

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

Você pode usá-los perfeitamente com funções anônimas:

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

Agora, lê-se facilmente como "após N milissegundos ..." (ou "a cada N milissegundo ...")

1j01
fonte
2

Outra maneira de fazer isso é usando Promise e setTimeout (observe que você precisa estar dentro de uma função e configurá-la como assíncrona com a palavra-chave async):

async yourAsynchronousFunction () {

    var a = 1+3;

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

    var b = a + 4;

}
Ostn
fonte
2

Aqui está uma maneira muito simples de fazê-lo que "parece" uma pausa / suspensão síncrona, mas é um código js assí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');
}

user2330237
fonte
1

Você pode usar javascript simples, isso chamará sua função / método após 5 segundos:

setTimeout(()=> { your_function(); }, 5000);
O cavalo preto
fonte
0

Existem várias maneiras de resolver esse problema. Se usarmos a setTimeoutfunção, vamos conhecê-la primeiro. Esta função possui três parâmetros: functionou code, delay(em milissegundos) e o parameters. Como a função ou o parâmetro de código é necessário, os outros são opcionais. Depois de não ter inserido o atraso , ele será definido como zero.

Para mais detalhes sobre a setTimeout() ir para este link .

Versão simplificada:

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

saída:
a = 4
24 -> Identificador de número da lista de tempos limites ativos
b = 8


Usando o parâmetro pass:

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);
}

saída:
a = 4
25 -> Identificador de número da lista de tempos limites ativos
b = 8



Suporte do navegador:

Chrome Firefox Edge Safari Opera
1,0 1,0 4,0 1,0 4,0
Alexandre Neukirchen
fonte
0

Este é o meu modelo que mostra como "dormir" ou "DoEvents" em javascript usando uma função geradora (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
fonte
0

Tente esta função:

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

Aqui está como você o usa:

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

Ou vá com estilo promissor:

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

Aqui está uma demonstração .

Richie Bendall
fonte