Jak mogę czekać W Node.js (Javascript) muszę wstrzymać się na pewien czas


385

Tworzę konsolę podobną do skryptu na potrzeby osobiste. Muszę być w stanie wstrzymać się na dłuższy czas, ale z moich badań wynika, że ​​node.js nie ma możliwości zatrzymania się zgodnie z wymaganiami. Po pewnym czasie trudno jest odczytać informacje o użytkownikach ... Widziałem trochę kodu, ale uważam, że muszą mieć w sobie inny kod, aby działały, takie jak:

setTimeout(function() {
}, 3000);

Potrzebuję jednak wszystkiego po tym wierszu kodu, aby wykonać po pewnym czasie.

Na przykład,

//start-of-code
console.log('Welcome to My Console,');
some-wait-code-here-for-ten-seconds..........
console.log('Blah blah blah blah extra-blah');
//endcode. 

Widziałem też takie rzeczy

yield sleep(2000);

Ale node.js tego nie rozpoznaje.

Jak mogę osiągnąć tę dłuższą przerwę?


3
o tobie oznacz jedną z tych osób jako poprawną odpowiedź :)
Billybonks

1
@Christopher Allen, Może nie dotyczy, ale wykonuje pracę: require("child_process").execSync('php -r "sleep($argv[1]);" ' + seconds);
Haitham Sweilem

Node-sen moduł npm może rade (jednak chciałbym używać go tylko do debugowania)
Julian Soro

Odpowiedzi:


211

Najlepszym sposobem na to jest podzielenie kodu na wiele funkcji, takich jak:

function function1() {
    // stuff you want to happen right away
    console.log('Welcome to My Console,');
}

function function2() {
    // all the stuff you want to happen after that pause
    console.log('Blah blah blah blah extra-blah');
}

// call the first chunk of code right away
function1();

// call the rest of the code and have it execute after 3 seconds
setTimeout(function2, 3000);

Jest podobny do rozwiązania JohnnyHK , ale o wiele ładniejszy i łatwiejszy do rozszerzenia.


7
@LucasSeveryn Robisz więc coś złego. Jest to podstawowy wzór projektowy.
Elliot Bonneville,

A co robisz, gdy uruchomienie funkcji nie jest tylko jedną funkcją, ale 10 plików od kodu, który należy zdezynchronizować?
Cyril Duchon-Doris

10
@ CyrilDuchon-Doris What?
AturSams

3
użyj odpowiedzi @ machineghost, aby uzyskać eleganckie rozwiązanie oparte na obietnicach, które nie wymaga niestandardowego kodu i obsługuje oczekiwanie / asynchronizację
Dane Macaulay

Jest to asynchroniczne, oznacza to, że jeśli funkcja 1 zakończy się przed 3 sekundami, zaczną się nakładać. Wtedy nie możesz nawet wrócić i prawie nigdy nie można go użyć. Jedynym sposobem, jaki do tej pory znalazłem, było użyciedebugger;
Cristian Traìna

427

Nowa odpowiedź na stare pytanie. Dzisiaj ( styczeń 2017 czerwca 2019) jest znacznie łatwiej. Możesz użyć nowej async/awaitskładni . Na przykład:

async function init() {
  console.log(1);
  await sleep(1000);
  console.log(2);
}

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

Aby używać async/awaitod razu po instalacji bez instalacji i wtyczek, musisz użyć node-v7 lub node-v8, używając --harmonyflagi.

Aktualizacja czerwca 2019 r .: Korzystając z najnowszych wersji NodeJS, możesz go używać od razu po wyjęciu z pudełka. Nie trzeba podawać argumentów wiersza poleceń. Nawet Google Chrome obsługuje go dzisiaj.

Aktualizacja maj 2020: wkrótce będzie można używać awaitskładni poza funkcją asynchroniczną. Na najwyższym poziomie, jak w tym przykładzie

await sleep(1000)
function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
} 

Wniosek znajduje się na etapie 3 . Możesz z niego korzystać dzisiaj, używając webpacka 5 (alfa),

Więcej informacji:


5
Myślę, że zapomniałeś awaitsłowa kluczowego przedsleep(1000)
Ryan Jarvis

6
To naprawdę powinna być odpowiedź, pakiet sleep node.js może być kłopotliwy.
Stuart Allen,

4
Rzeczywiście najlepsze rozwiązanie
Kostas Siabanis,

40
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));Dla maniaków oneliner jak ja :)
Predrag Stojadinović

21
let sleep = require('util').promisify(setTimeout);działa na węźle 7.6+ i poprawia czytelność
BrianHVB

216

Najkrótsze rozwiązanie bez żadnych zależności:

await new Promise(resolve => setTimeout(resolve, 5000));

14
„Wysokością wyrafinowania jest prostota”. - Clare Booth Luce. To zdecydowanie najlepsza odpowiedź, IMO.
Arnold

3
Jest to oczywiście znacznie nowsze niż inne odpowiedzi, ale jest to najbardziej elegancki od 2018 r., Który wykonuje pracę w 1 linii bez żadnego innego wpływu na kod.
Herick,

1
Ten jest dobry. Musisz jednak używać NodeJS w wersji 7.6.0 lub nowszej. Nie będzie działać na starszych wersjach.
Ryan Shillington

5
let sleep = require('util').promisify(setTimeout);to trzy znaki dłuższe, ale wielokrotnego użytku i bardziej czytelne imo
BrianHVB

2
Aby uniknąć reklamacji eslint, zadzwoń resolvezamiast niej done. Czyliawait new Promise(resolve => setTimeout(resolve, 5000))
Darren Gotować

150

Wpisz kod, który chcesz wykonać po opóźnieniu w ciągu setTimeout wywołania zwrotnego:

console.log('Welcome to My Console,');
setTimeout(function() {
    console.log('Blah blah blah blah extra-blah');
}, 3000);

2
Jest to okropnie niechlujna i ogólnie zła praktyka, szczególnie jeśli PO chce, aby reszta programu działała po tym opóźnieniu. Zobacz moją odpowiedź.
Elliot Bonneville

32
@ElliotBonneville To tylko przykład ilustrujący tę koncepcję. Oczywiście można (należy) uwzględnić kod w wywołaniu funkcji zamiast używać kodu wbudowanego, tak jak gdziekolwiek indziej.
JohnnyHK,

@ChristopherKemp: Okazuje się, że Node.js ma na to rozwiązanie o nazwie node-fibers. Sprawdź to.
Elliot Bonneville,

To świetne rozwiązanie, szczególnie jeśli nie chcesz wykonywać żadnego kodu, po prostu chcesz naśladować metodę „uśpienia” w pętli. Nic złego w tym przykładzie.
Halfstop,

jest to idealne do opóźniania żądań przychodzących od klienta, zastosowałem to podejście do przetestowania moich ładujących spinnerów po stronie klienta
moein rahimi

145

Jest to prosta technika blokowania:

var waitTill = new Date(new Date().getTime() + seconds * 1000);
while(waitTill > new Date()){}

To blokowanie , o ile nic innego nie będzie się działo w skrypcie (jak wywołania zwrotne). Ale ponieważ jest to skrypt konsoli, być może jest to, czego potrzebujesz!


27
okropne czy nie, robi proste wait, blokuje i działa w jakimś celu testowym. Dokładnie tego szukałem.
Clijsters,

8
doskonale odpowiada na pytanie bez bibliotek stron trzecich i jest prosty. Jednak ludzie mówią „straszne” .... To jest wielki przykład do symulowania duże obciążenie procesora itd. Btw bardzo podobna do tej phpied.com/sleep-in-javascript
Don Cheadle

2
Jest to również wymagane podejście, jeśli próbujesz symulować coś, co spowolniłoby pętlę zdarzeń, spowalniając w ten sposób innych użytkowników / połączeń. Dodanie opóźnionego oddzwaniania nie wpłynie na innych użytkowników / połączeń.
Don Cheadle

13
Jest to spin-wait.
Mike Atlas,

7
@Ali, który wydaje się być celem.
Félix Gagnon-Grenier

63

W węźle 7.6.0 lub nowszym

Węzeł obsługuje natywne oczekiwanie:

const sleep = (waitTimeInMs) => new Promise(resolve => setTimeout(resolve, waitTimeInMs));

to jeśli możesz użyć funkcji asynchronicznych:

await sleep(10000); // sleep for 10 seconds

lub:

sleep(10000).then(() => {
  // This will execute 10 seconds from now
});

W starszych wersjach węzłów (oryginalna odpowiedź)

Chciałem asynchronicznego trybu uśpienia, który działał w systemach Windows i Linux, bez konieczności zapychania mojego procesora długą pętlą while. Próbowałem pakietu uśpienia, ale nie można go zainstalować na moim komputerze z systemem Windows. Skończyło się na tym, że:

https://www.npmjs.com/package/system-sleep

Aby go zainstalować, wpisz:

npm install system-sleep

W twoim kodzie

var sleep = require('system-sleep');
sleep(10*1000); // sleep for 10 seconds

Działa jak marzenie.


1
świetna odpowiedź, dziękuję - umożliwił niemożliwy kod - to NIE jest spin-wait
danday74

1
Podczas dalszych badań moduł ten opiera się na deasync, który został opracowany z innego repozytorium github deasync. Oryginalne repo ostrzega, aby nie używać go, ponieważ jest to hack. Działa, ale nie na wszystkich platformach, więc jeśli potrzebujesz rozwiązania wieloplatformowego, unikaj tego.
danday74

1
tak, nigdy nie spotkałem się z problemami i jest to świetne dla programistów - pod maską wierzę, że opiera się na C lub C ++, który jest szeroko dostępny, ale chyba na niektórych maszynach nie jest i to wtedy, gdy zawodzi, dlatego powoduje problemy - jeśli się nie powiedzie, system przestaje
działać

1
Ten pakiet nie działa w systemie Mac OS, a zatem nie jest kompatybilny krzyżowo i dlatego nie jest wykonalny. Zobacz github.com/jochemstoel/nodejs-system-sleep/issues/4
nuzzolilo

1
@Nuzzolilo To dziwne. Mamy jednego programistę w systemie Mac OS i nigdy nie wspominał o niczym złym. Podejrzewam, że jest to konkretna wersja Mac OS. Zaktualizowałem również tę odpowiedź, ponieważ tryb uśpienia jest zasadniczo wbudowany w nowsze wersje NodeJS.
Ryan Shillington

62

Prosta i elegancka funkcja snu z wykorzystaniem nowoczesnego Javascript

function sleep(millis) {
    return new Promise(resolve => setTimeout(resolve, millis));
}

Bez zależności, bez piekła zwrotnego; Otóż ​​to :-)


Biorąc pod uwagę przykład podany w pytaniu, w ten sposób chcielibyśmy spać między dwoma dziennikami konsoli:

async function main() {
    console.log("Foo");
    await sleep(2000);
    console.log("Bar");
}

main();

„Wada” polega na tym, że teraz musi być asyncrównież twoja główna funkcja . Ale biorąc pod uwagę, że już piszesz nowoczesny kod JavaScript, prawdopodobnie (lub przynajmniej powinieneś!) Używasz async/ awaitcałego kodu, więc to naprawdę nie jest problem. Wszystkie współczesne przeglądarki obsługują obecnie to.

Dając trochę wglądu w sleepfunkcję dla tych, którzy nie są przyzwyczajeni async/awaiti operatorzy grubych strzał, jest to pełny sposób pisania:

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

Jednak użycie operatora grubych strzałek czyni go jeszcze mniejszym (i bardziej eleganckim).


1
Możesz także napisać funkcję uśpienia bez asynci pozostawiając wszystko inne bez zmian. Prawdopodobnie jest to jednak wyraźniejsze async.
Kevin Peña

Dobry chwyt, @ KevinPeña. W rzeczywistości myślę, że wolę to bez async. Edytowałem moją odpowiedź z twoją sugestią. Nie sądzę, żeby kod był bardziej przejrzysty; w tym celu skorzystałbym zamiast tego z JSDoc.
Lucio Paiva

8
W moich skryptach lubię mieć następujący linijka:const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
korya


34

To pytanie jest dość stare, ale ostatnio V8 dodało Generatory, które mogą spełnić wymagania OP. Generatory są zazwyczaj najłatwiejsze w użyciu do interakcji asynchronicznych z pomocą biblioteki, takiej jak zawieszenie lub uruchomienie generatora .

Oto przykład użycia zawieszenia:

suspend(function* () {
    console.log('Welcome to My Console,');
    yield setTimeout(suspend.resume(), 10000); // 10 seconds pass..
    console.log('Blah blah blah blah extra-blah');
})();

Literatura pokrewna (poprzez bezwstydną autopromocję): O co chodzi z generatorami? .


2
Dobra odpowiedź - Ale powinienem przeczytaćyield setTimeout(suspend.resume(), 10000);
edhubbell

1
Dzięki, @edhubbell. Ta odpowiedź była oparta na bardzo starej wersji zawieszenia, ale masz rację co do najnowszej. Zaktualizuję odpowiedź.
jmar777

dla jakiej wersji jest to węzeł?
danday74

1
@ danday74 Nie pamiętam dokładnie, kiedy nie zostały oflagowane, ale są już od wersji v0.12 za --harmonyflagą i według node.green są przynajmniej dostępne bez żadnych flag od wersji v4.8.4: node.green/#ES2015-functions-generators-basic-functionality . Należy jednak pamiętać, że nowsza async/awaitskładnia zapewnia teraz lepsze rozwiązanie tego problemu, bez potrzeby korzystania z dodatkowych bibliotek, takich jak suspend. Zobacz tę odpowiedź na przykład: stackoverflow.com/a/41957152/376789 . Funkcje asynchroniczne są dostępne (bez flag) od wersji 7.10.
jmar777

20

W systemie Linux / nodejs działa to dla mnie:

const spawnSync = wymagany ('child_process'). spawnSync;

var sleep = spawnSync („sleep”, [1.5]);

Blokuje, ale nie jest zajęta pętla oczekiwania.

Określony czas jest w sekundach, ale może być ułamkiem. Nie wiem, czy inne systemy operacyjne mają podobne polecenia.


sleepjest prawie wszechobecny i jest wszechstronny od wczesnych dni UNIXa en.wikipedia.org/wiki/Sleep_%28Unix%29 . Tylko „nie rzadko” OS to może nie istnieć to okna (jednak nie można próbowaćchild_process.spawnSync('timeout', ['/T', '10'])
humanityANDpeace

17

Jeśli chcesz „kodować golfa”, możesz utworzyć krótszą wersję niektórych innych odpowiedzi tutaj:

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

Ale naprawdę idealną odpowiedzią jest moim zdaniem użycie utilbiblioteki Node i jej promisifyfunkcji, która została zaprojektowana właśnie do tego rodzaju rzeczy (tworzenie opartych na obietnicach wersji wcześniej istniejących rzeczy nieobjętych obietnicą):

const util = require('util');
const sleep = util.promisify(setTimeout);

W obu przypadkach można następnie wstrzymać, używając funkcji awaitwywoływania sleep:

await sleep(1000); // sleep for 1s/1000ms

W rzeczywistości przy użyciu funkcji util.promisify nie można wywołać trybu uśpienia bez zapewnienia oddzwonienia. nodejs.org/api/…
Howie

1
Tęsknisz za await. W pewnym sensie tworzy wywołanie zwrotne, wstrzymuje rzeczy, a następnie ponownie uruchamia kod, gdy to wywołanie zwrotne powróci. Oddzwanianie powraca z wartością, ale w tym przypadku nie obchodzi nas to, więc nie ma nic po lewej stronie await.
machineghost

1
napisz go w jednym wierszu, aby napisać kod golfa;) const sleep = wymagana ('util'). promisify (setTimeout);
Fabian

14

Niedawno stworzyłem prostszą abstrakcję o nazwie wait.for aby wywoływać funkcje asynchroniczne w trybie synchronizacji (na podstawie włókien węzłowych). Istnieje również wersja oparta na nadchodzących generatorach ES6.

https://github.com/luciotato/waitfor

Korzystanie z wait.for można wywoływać dowolną standardową funkcję asynchroniczną nodejs, tak jakby to była funkcja synchronizacji, bez blokowania pętli zdarzeń węzła.

Możesz kodować sekwencyjnie, kiedy jest to potrzebne, co jest (tak sądzę) idealne, aby uprościć skrypty do użytku osobistego.

za pomocą wait.for twój kod będzie:

require('waitfor')

..in a fiber..
//start-of-code
console.log('Welcome to My Console,');
wait.miliseconds(10*1000); //defined in waitfor/paralell-tests.js - DOES NOT BLOCK
console.log('Blah blah blah blah extra-blah');
//endcode. 

W trybie synchronizacji można również wywołać dowolną funkcję asynchroniczną . Sprawdź przykłady.


TypeError: Object #<Object> has no method 'miliseconds'
CaffeineAddiction

komentarz mówi: „// zdefiniowano w waitfor / paralell-tests.js” pobierz go z tego pliku.
Lucio M. Tato

2
po otrzymaniu go wait.for/paralell-tests.jsnapotkałem kolejne błędy związane z niezdefiniowanymi właściwościami itp. Więc musiałem je również skopiować. Dlaczego nie zorganizujesz kodu w taki sposób, aby nie był wymagany?
user907860

Wait.for i inne rozwiązania światłowodowe otworzyły dla mnie zupełnie nowy świat! Głosowałbym za tym milion razy, gdybym mógł. Chociaż większość społeczności nodejs jest przeciwna włóknom, myślę, że są fantastycznym dodatkiem i zdecydowanie mają swoje miejsce, jeśli chodzi o piekło zwrotne.
Levi Roberts

7

Ponieważ silnik javascript (v8) uruchamia kod na podstawie sekwencji zdarzeń w kolejce zdarzeń, nie ma ścisłego wymogu, aby javascript dokładnie uruchamiał wykonywanie po określonym czasie. Oznacza to, że po ustawieniu kilku sekund na wykonanie kodu później kod wyzwalający opiera się wyłącznie na sekwencji w kolejce zdarzeń. Wywołanie wykonania kodu może więc zająć więcej niż określony czas.

Więc Node.js podąża

process.nextTick()

aby uruchomić kod później zamiast setTimeout (). Na przykład,

process.nextTick(function(){
    console.log("This will be printed later");
});

2

Dzięki obsłudze ES6 Promisemożemy z nich korzystać bez żadnej pomocy strony trzeciej.

const sleep = (seconds) => {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, (seconds * 1000));
    });
};

// We are not using `reject` anywhere, but it is good to
// stick to standard signature.

Następnie użyj go w następujący sposób:

const waitThenDo(howLong, doWhat) => {
    return sleep(howLong).then(doWhat);
};

Zauważ, że doWhatfunkcja staje się resolvewywołaniem zwrotnym w obrębie new Promise(...).

Zauważ też, że jest to sen ASYNCHRONICZNY. Nie blokuje pętli zdarzeń. Jeśli potrzebujesz blokowania snu, skorzystaj z tej biblioteki, która realizuje blokowanie snu za pomocą powiązań C ++. (Chociaż potrzeba blokowania snu w Węzłach, takich jak środowiska asynchroniczne, jest rzadka).

https://github.com/erikdubbelboer/node-sleep


1
let co = require('co');
const sleep = ms => new Promise(res => setTimeout(res, ms));

co(function*() {
    console.log('Welcome to My Console,');
    yield sleep(3000);
    console.log('Blah blah blah blah extra-blah');
});

Powyższy kod jest efektem ubocznym rozwiązania problemu asynchronicznego wywołania zwrotnego asynchronicznego kodu JavaScript. Jest to również powód, dla którego uważam, że JavaScript jest przydatnym językiem w backend. Moim zdaniem jest to najbardziej ekscytujące ulepszenie wprowadzone we współczesnym Javascript. Aby w pełni zrozumieć, jak to działa, należy w pełni zrozumieć sposób działania generatora. Słowo functionkluczowe, po którym następuje a, *nazywa się funkcją generatora we współczesnym Javascript. Pakiet npmco funkcję Runner do uruchamiania generatora.

Zasadniczo funkcja generatora zapewniła sposób na wstrzymanie wykonania funkcji ze yieldsłowem kluczowym, a jednocześnie yieldw funkcji generatora umożliwiła wymianę informacji między generatorem a dzwoniącym. Dało to mechanizmowi wywołującemu wyodrębnienie danych z promisepołączenia asynchronicznego i przekazanie rozstrzygniętych danych z powrotem do generatora. Skutecznie sprawia, że ​​połączenie asynchroniczne jest synchroniczne.


Chociaż ten kod może odpowiedzieć na pytanie, zapewnienie dodatkowego kontekstu dotyczącego tego, jak i / lub dlaczego rozwiązuje problem, poprawiłoby długoterminową wartość odpowiedzi.
Kaczor Donald

Dzięki @DonaldDuck. Kod w mojej odpowiedzi jest prawdopodobnie najbardziej ekscytującą częścią ulepszenia w Javascript. Jestem tak zaskoczony, że niektórzy super mądrzy ludzie myślą o tym sposobie rozwiązania problemu z piekłem zwrotnym.
Qian Chen,


0

Jeśli potrzebujesz tylko zawiesić w celu testowania, bieżące wykonanie wątku spróbuj:

function longExecFunc(callback, count) {

    for (var j = 0; j < count; j++) {
        for (var i = 1; i < (1 << 30); i++) {
            var q = Math.sqrt(1 << 30);
        }
    }
    callback();
}
longExecFunc(() => { console.log('done!')}, 5); //5, 6 ... whatever. Higher -- longer

0

po prostu poczekamy 5 sekund, aby zdarzyło się jakieś zdarzenie (byłoby to wskazane przez zrobioną zmienną ustawioną na true w innym miejscu w kodzie) lub gdy upłynie limit czasu, że sprawdzamy co 100 ms

    var timeout=5000; //will wait for 5 seconds or untildone
    var scope = this; //bind this to scope variable
    (function() {
        if (timeout<=0 || scope.done) //timeout expired or done
        {
            scope.callback();//some function to call after we are done
        }
        else
        {
            setTimeout(arguments.callee,100) //call itself again until done
            timeout -= 100;
        }
    })();


0

Zamiast robić wszystkie te setTimeout, spać i wiele innych rzeczy, lepiej użyć

const delay = require('delay');
await delay(2000); // will wait for 2 seconds before executing next line of code

Czy możesz wyjaśnić, co to jest „opóźnienie” i link do jego dokumentów? Dlaczego to jest lepsze?
boycy

-2

Inne odpowiedzi są świetne, ale pomyślałem, że wybiorę inny takt.

Jeśli naprawdę szukasz jedynie spowolnienia określonego pliku w systemie Linux:

 rm slowfile; mkfifo slowfile; perl -e 'select STDOUT; $| = 1; while(<>) {print $_; sleep(1) if (($ii++ % 5) == 0); }' myfile > slowfile  &

węzeł slowprile mójprog

Będzie to spało 1 sekundę co pięć linii. Program węzła będzie działał tak wolno, jak program piszący. Jeśli robi inne rzeczy, będą kontynuować z normalną prędkością.

Program mkfifo tworzy potok typu „pierwsze weszło, pierwsze wyszło”. To sprawia, że ​​to działa. Linia perla napisze tak szybko, jak chcesz. $ | = 1 mówi, że nie buforuj danych wyjściowych.


-3

Po przeczytaniu odpowiedzi na to pytanie stworzyłem prostą funkcję, która może również wykonać oddzwonienie, jeśli potrzebujesz:

function waitFor(ms, cb) {
  var waitTill = new Date(new Date().getTime() + ms);
  while(waitTill > new Date()){};
  if (cb) {
    cb()
  } else {
   return true
  }
}

-4

Aby uzyskać więcej informacji na temat

yield sleep(2000); 

powinieneś sprawdzić Redux-Saga . Ale jest to specyficzne dla twojego wyboru Redux jako modelu modelu (choć absolutnie nie jest to konieczne).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.