Sleep in JavaScript - opóźnienie między akcjami

129

Czy jest sposób, aby przespać JavaScript, zanim wykona on inną akcję?

Przykład:

 var a = 1+3;
 // Sleep 3 seconds before the next action here
 var b = a + 4;
Jin Yong
źródło

Odpowiedzi:

140

Możesz użyć, setTimeoutaby osiągnąć podobny efekt:

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

Tak naprawdę nie „usypia” JavaScript - po prostu wykonuje funkcję przekazaną setTimeoutpo określonym czasie (określonym w milisekundach). Chociaż możliwe jest napisanie funkcji uśpienia dla JavaScript, najlepiej jest z niej korzystać, setTimeoutjeśli to możliwe, ponieważ nie zamraża ona wszystkiego w okresie snu.

Steve Harrison
źródło
9
Przyjrzyj się także setInterval (). Jest podobna do setTimeout (), ale twoja funkcja jest wywoływana wiele razy (dopóki jej nie zatrzymasz), co jest przydatne, jeśli chcesz coś zrobić podczas snu (np. Aktualizować postęp, utrzymywać stan wewnętrzny lub cokolwiek innego).
Anders Sandvig
5
To nie odpowiada na pytanie. Pytanie dotyczy odpowiednika „snu”, którym tak nie jest.
felwithe
Chociaż ta odpowiedź nie jest zgodna z tym, co zadano pytanie, ale jest bardziej użyteczna niż pętla i porównanie Date.now (). Nikt nie używa zablokowanej pętli uśpienia narzędzia.
Li Chunlin,
2
Chyba że, oczywiście, pętla blokująca jest dokładnie tym , czego ktoś chce.
Wygrał Sane
55

Na wypadek, gdybyś naprawdę sleep()chciał coś przetestować. Należy jednak pamiętać, że podczas debugowania najczęściej powoduje awarię przeglądarki - prawdopodobnie dlatego i tak jest to potrzebne. W trybie produkcyjnym wykomentuję tę funkcję.

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

Nie używaj new Date()w pętli, chyba że chcesz marnować pamięć, moc obliczeniową, baterię i ewentualnie żywotność urządzenia.

Rodrigo
źródło
8
Ta odpowiedź zasługuje na więcej głosów. Pierwsze pytanie jest przyczyną tej odpowiedzi.
jagc
a co z ostrzeżeniem o „zbyt dużej rekursji”?
Oki Erie Rinaldi
1
@OkiErieRinaldi Nie ma tam rekursji, to tylko pętla.
Rodrigo,
7
@ 3.1415926535897932384626433833 Cóż, ktoś poprosił o funkcję „uśpienia”, oto co tutaj jest. Użyłem go raz, nie pamiętam dokładnie, do jakiego rodzaju debugowania. Jeśli kiedykolwiek będę tego potrzebował, dokładnie wiem, gdzie go znaleźć. Jeśli wolisz inną funkcję, to Twój wybór. Czy nie jest wspaniale móc wybierać?
Rodrigo
2
„Czekam zajęty”.
Zeek2
13

Wersja ECMAScript 6 wykorzystująca generatory z wydajnością do „blokowania kodu”:

Ponieważ oryginalne pytanie zostało wysłane siedem lat temu, nie zawracałem sobie głowy udzieleniem dokładnego kodu, ponieważ jest po prostu zbyt łatwe i już na nie odpowiedziałem. Powinno to pomóc w bardziej skomplikowanych problemach, na przykład jeśli potrzebujesz co najmniej dwóch uśpień lub jeśli planujesz sekwencyjne wykonywanie asynchroniczne. Możesz go zmodyfikować, aby dopasować go do swoich potrzeb.

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

funkcjonować*

() => funkcja strzałki

Możesz także łączyć wydarzenia za pomocą Obietnic :

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

Albo byłby znacznie prostszy i mniej wyszukany sposób

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

Jeśli tylko czekasz na wystąpienie jakiegoś warunku, możesz poczekać w ten sposób

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é
źródło
11

Aktualizacja 2018

Najnowsze przeglądarki Safari, Firefox i Node.js obsługują teraz również async / await / promises.

Korzystanie z async / await / Promises:

(Od 1/2017 obsługiwane w przeglądarce Chrome, ale nie w 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();

Aktualizacja 2017

JavaScript ewoluował od czasu zadania tego pytania i ma teraz funkcje generatora, a nowy async / await / Promise jest wdrażany. Poniżej znajdują się dwa rozwiązania, jedno z funkcją generatora, które będzie działać we wszystkich nowoczesnych przeglądarkach, a drugie, wykorzystujące nowy async / await, który nie jest jeszcze wszędzie obsługiwany.

Korzystanie z funkcji generatora:

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

Zwróć uwagę, że oba te rozwiązania mają charakter asynchroniczny. Oznacza to, że myAsyncFunc (w obu przypadkach) powróci podczas snu.

Należy zauważyć, że to pytanie jest inne niż Jaka jest wersja funkcji sleep () w języku JavaScript? gdzie żądający prosi o prawdziwy sen (brak wykonania innego kodu w procesie) zamiast opóźnienia między akcjami.

niry
źródło
1
Jak dotąd najlepsza odpowiedź !! Spędziłem 30 minut szukając wszędzie, aby znaleźć to .. wielkie dzięki !!!
538ROMEO
1
Brakowało mi tej odpowiedzi, szukając rozwiązania i wymyśliłem rower na nowo: D Gdybym tylko zobaczył go wcześniej, zaoszczędziłoby mi to wiele godzin !! Głosowano!
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(); };pozwoliłoby ci let asyncAdd = co(function* (a, b) { console.log('Sleeping'); yield sleep(3000); console.log('Done'); return a + b; }); asyncAdd(3, 4).then(console.log);użyć definicji sleep()z drugiego bloku kodu.
Patrick Roberts
3

Jeśli potrzebujesz mniej niezgrabnych funkcji niż setTimeouti setInterval, możesz opakować je w funkcje, które po prostu odwracają kolejność argumentów i nadają im ładne nazwy:

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

Wersje CoffeeScript:

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

Następnie możesz ich ładnie używać z anonimowymi funkcjami:

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

Teraz można go łatwo odczytać jako „po N milisekundach…” (lub „co N milisekundach…”)

1j01
źródło
2

Innym sposobem jest użycie Promise i setTimeout (pamiętaj, że musisz być wewnątrz funkcji i ustawić ją jako asynchroniczną ze słowem kluczowym async):

async yourAsynchronousFunction () {

    var a = 1+3;

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

    var b = a + 4;

}
Ostn
źródło
2

Oto bardzo prosty sposób na zrobienie tego, który `` czuje się '' jak synchroniczny sen / pauza, ale jest legalnym kodem asynchronicznym js.

// 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
źródło
1

Możesz użyć zwykłego javascript, to wywoła twoją funkcję / metodę po 5 sekundach:

setTimeout(()=> { your_function(); }, 5000);
Czarny koń
źródło
0

Istnieje kilka sposobów rozwiązania tego problemu. Jeśli korzystamy z setTimeoutfunkcji, najpierw ją poznajmy. Ta funkcja ma trzy parametry: functionlub code, delay(w milisekundach) i parameters. Ponieważ parametr funkcji lub kodu jest wymagany, pozostałe są opcjonalne. Jeśli nie wprowadziłeś opóźnienia , zostanie ono ustawione na zero.

Aby uzyskać więcej informacji na temat setTimeout() tego łącza .

Wersja uproszczona:

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

wyjście:
a = 4
24 -> Identyfikator numeryczny listy aktywnych timeoutów
b = 8


Korzystanie z parametru 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);
}

wyjście:
a = 4
25 -> Identyfikator numeru listy aktywnych timeoutów
b = 8



Obsługa przeglądarek:

Chrome Firefox Edge Safari Opera
1,0 1,0 4,0 1,0 4,0
Alexandre Neukirchen
źródło
0

To jest mój model, który pokazuje, jak "spać" lub "DoEvents" w javascript przy użyciu funkcji generatora (ES6). Skomentowany kod:

<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
źródło
0

Wypróbuj tę funkcję:

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

Oto jak go używasz:

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

Lub obiecaj styl:

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

Oto demo .

Richie Bendall
źródło