Як я можу чекати в Node.js (Javascript), мені потрібно зробити паузу протягом певного періоду часу


385

Я розробляю консоль типу сценарію для особистих потреб. Мені потрібно мати змогу зробити паузу протягом тривалого часу, але, з мого дослідження, node.js не може зупинитись, як потрібно. Через певний проміжок часу стає важко читати інформацію користувачів ... Я бачив якийсь код там, але я вважаю, що вони повинні мати інший код всередині них, щоб вони працювали, наприклад:

setTimeout(function() {
}, 3000);

Однак мені потрібно все після цього рядка коду виконати через проміжок часу.

Наприклад,

//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. 

Я також бачив такі речі

yield sleep(2000);

Але node.js не визнає цього.

Як я можу досягти цієї розширеної паузи?


3
Як про те, що ти
позначиш

1
@Christopher Allen, можливо, це не стосується, але виконує роботу: require("child_process").execSync('php -r "sleep($argv[1]);" ' + seconds);
Haitham Sweilem

Вузла сну модуль НПМ може зробити трюк (проте, я хотів би використовувати його тільки для налагодження)
Джуліан Соро

Відповіді:


211

Найкращий спосіб зробити це - розбити код на кілька функцій, наприклад:

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

Це схоже на рішення JohnnyHK , але набагато акуратніше і легше розширити.


7
@LucasSeveryn Тоді ви щось робите не так. Це основна модель дизайну.
Елліот Бонневіль

І що ви робите, коли старт функції не лише однієї функції, а 10 файлів від коду, який потрібно деасинхронізувати?
Кирило Дюшон-Доріс

10
@ CyrilDuchon-Doris Що?
AturSams

3
використовувати @ machineghost відповідь для елегантного рішення, заснованого на обіцянках, яке не потребує спеціального коду та підтримує очікування / асинхронізація
Dane Macaulay

Це асинхронізація, це означає, що якщо функція1 припиняється до 3 секунд, вони починають перекриватись один одним. Тоді ви навіть не можете повернутися, і це майже ніколи не можна використовувати. Єдиний спосіб, який я до цього часу знайшов - це використанняdebugger;
Крістіан Трайна

427

Нова відповідь на старе запитання. Сьогодні ( січень 2017 червень 2019 року) набагато простіше. Ви можете використовувати новий async/awaitсинтаксис . Наприклад:

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

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

Для використання async/awaitпоза вікном без встановлення та плагінів вам потрібно використовувати node-v7 або node-v8, використовуючи --harmonyпрапор.

Оновлення червня 2019 року. Використовуючи останні версії NodeJS, ви можете використовувати їх поза коробкою. Не потрібно наводити аргументи командного рядка. Навіть Google Chrome підтримує його сьогодні.

Оновлення травня 2020 року: незабаром ви зможете використовувати awaitсинтаксис поза функцією асинхронізації. На верхньому рівні, як у цьому прикладі

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

Пропозиція знаходиться на 3 етапі . Ви можете використовувати його сьогодні, використовуючи webpack 5 (alpha),

Більше інформації:


5
Я думаю, ви забули awaitключове слово передsleep(1000)
Ryan Jarvis

6
Це справді має бути відповіддю, пакет сну node.js може виявити клопіт.
Стюарт Аллен

4
Найкраще рішення
Костас Сяаніс

40
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));Для oneliner виродків, як я :)
Predrag Stojadinović

21
let sleep = require('util').promisify(setTimeout);працює над Node 7.6+ та покращує читабельність
BrianHVB

216

Найкоротше рішення без будь-яких залежностей:

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

14
"Висота витонченості - простота". - Клер Бут Люс. Це, безумовно, найкраща відповідь, ІМО.
Арнольд

3
Це, очевидно, набагато пізніші за інші відповіді, але це найелегантніше з 2018 року, яке виконує роботу в 1 рядок без будь-яких інших впливів на код.
Герик

1
Це добре. Вам потрібно використовувати NodeJS 7.6.0 або вище. Він не працюватиме на старих версіях.
Райан Шиллінгтон

5
let sleep = require('util').promisify(setTimeout);на три символи довший, але багаторазовий і більш читабельний imo
BrianHVB

2
Щоб уникнути скарги на еллінт, зателефонуйте їй resolveзамість done. Тобтоawait new Promise(resolve => setTimeout(resolve, 5000))
Даррен Кук

150

Помістіть код, який потрібно виконати після затримки, в межах setTimeoutзворотного дзвінка:

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

2
Це жахливо безладно і, як правило, погана практика, особливо якщо ОП хоче, щоб решта програми була запущена після цієї затримки. Дивіться мою відповідь.
Елліот Бонневіль

32
@ElliotBonneville Це просто приклад для ілюстрації концепції. Очевидно, ви можете (повинні) ввести код у виклик функції замість того, щоб використовувати вбудований код, як і деінде.
JohnnyHK

@ChristopherKemp: Виявляється, Node.js має рішення для цього, що називається node-fibers. Перевір.
Елліот Бонневіль

Це чудове рішення, особливо якщо ви не хочете виконувати жоден код, ви просто хочете імітувати метод "сну" всередині циклу. Нічого поганого в цьому прикладі.
Полустоп

це ідеально підходить для затримки запитів, що надходять від клієнта, я використовував цей підхід, щоб перевірити свої завантажувальні
спінери

145

Це проста техніка блокування:

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

Це блокується настільки, що більше нічого не відбудеться у вашому сценарії (як зворотні виклики). Але оскільки це консольний сценарій, можливо, це те, що вам потрібно!


27
жахливо чи ні, це робиться просто wait, він блокує, і це працює для певної мети тестування. Саме те, що я шукав.
Clijsters

8
прекрасно відповідає на питання без сторонніх ліфтів, і це просто. Але люди кажуть , що «жахливо» .... Це великий , наприклад , для імітації важкого навантаження на центральний процесор і т.д. Btw дуже схожий на цей phpied.com/sleep-in-javascript
Дон Чідл

2
Крім того, це необхідний підхід, якщо ви намагаєтеся імітувати щось, що сповільнило б цикл подій, тим самим сповільнивши й інших користувачів / підключення. Додавання зволіканих зворотних дзвінків не вплине на інших користувачів / з'єднання.
Don Cheadle

13
Це spin-wait.
Майк Атлас

7
@Ali, здається, це мета.
Фелікс Ганьон-Греньє

63

У Вузлі 7.6.0 або вище

Вузол підтримує очікування очікування:

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

то якщо ви можете використовувати функції асинхронізації:

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

або:

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

На старих версіях вузла (оригінальна відповідь)

Я хотів асинхронного сну, який працював у Windows & Linux, не затягуючи процесор довгим циклом. Я спробував спальний пакет, але він не встановився б у вікні Windows Я закінчив використовувати:

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

Щоб встановити його, введіть:

npm install system-sleep

У своєму коді

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

Працює як шарм.


1
чудова відповідь спасибі - це зробило неможливим якийсь код можливим - це НЕ
чаклування

1
При подальшому дослідженні цей модуль спирається на deasync, який розщеплений з іншого deasync github repo. Оригінальний репо застерігає не використовувати його, оскільки це хак. Це працює, але не на всіх платформах, тому, якщо вам потрібно рішення між платформами, уникайте цієї.
понеділок74,

1
так, я ніколи не відчував проб з цим, і це чудово для dev - під кришкою я вважаю, що він покладається на C або C ++, який є широко доступним, але я думаю, що на деяких машинах його немає, і це не вдається, тому це викликає проблеми - якщо це не вдасться, система сну повертається до
зачекання,

1
Цей пакет не працює на Mac OS, а тому не є сумісним, а тому не працює. Дивіться github.com/jochemstoel/nodejs-system-sleep/isissue/4
nuzzolilo

1
@Nuzzolilo Це дивно. У нас є один розробник на Mac OS, і він ніколи нічого не згадував. Я підозрюю, що це конкретна версія Mac OS. Я також оновив цю відповідь, оскільки сон в основному вбудований у новіші версії NodeJS.
Райан Шиллінгтон

62

Проста і елегантна функція сну за допомогою сучасного Javascript

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

Ніяких залежностей, жодного пекельного зворотного дзвінка; Це воно :-)


Розглядаючи приклад, наведений у питанні, ось як ми спали між двома журналами консолей:

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

main();

«Недолік» полягає в тому, що ваша основна функція тепер повинна бути asyncтакою ж. Але, враховуючи, що ви вже пишете сучасний код Javascript, ви, мабуть, (або принаймні повинні бути!) Використовуєте async/ у awaitвсьому коді, так що це справді не проблема. Всі сучасні браузери сьогодні його підтримують .

Давши трохи ознайомлення з sleepфункцією для тих, хто не звик до async/awaitоператорів і жирових стрілок, це багатослівний спосіб її написання:

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

Однак використання оператора жирової стрілки робить її ще меншою (і більш елегантною).


1
Ви також можете записати функцію сну, не asyncзалишаючи все інше. З цим, мабуть, зрозуміліше async.
Кевін Пенья

Хороший улов, @ KevinPeña. Справа фактично, я думаю, що я віддаю перевагу без нього async. Відредагував мою відповідь вашою пропозицією. Я не думаю, що це робить код більш зрозумілим; для цього я б вдався до JSDoc.
Лусіо Пайва

8
Мені подобається, що в моїх сценаріях є такий const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
однолінійний

46

Ви можете використовувати цей www.npmjs.com/package/sleep

var sleep = require('sleep');
sleep.sleep(10); // sleep for ten seconds

4
Він добре працює MacOS, але у ньому виникають помилки CentOSчерез помилки node_gyp . Це здається не портативним.
AechoLiu

1
можливо проблема, викликана не ОС, а побудовою вузла
Олексій

34

Це питання досить старе, але останнім часом V8 додав Генератори, які можуть виконати те, про що вимагав ОП. Генератори, як правило, найпростіше використовувати для асинхронних взаємодій за допомогою бібліотеки, наприклад, призупинити чи запустити ген .

Ось приклад використання призупинення:

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

Пов'язане читання (шляхом безсоромного самореклами): Що таке велика справа з генераторами? .


2
Гарна відповідь - Але варто прочитатиyield setTimeout(suspend.resume(), 10000);
edhubbell

1
Дякую, @edhubbell Ця відповідь ґрунтувалася на дуже старій версії призупинення, але ви маєте рацію щодо останньої. Я оновлю відповідь.
jmar777

для якої версії цього вузла?
понеділок74

1
@ daday74 Я не можу точно згадати, коли вони були відмічені, але вони існували з v0.12 позаду --harmonyпрапора, і згідно з node.green, вони принаймні доступні без жодних прапорів, починаючи з v4.8.4: node.green/#ES2015-functions-generators-basic-functionality . Зауважте, однак, що новіший async/awaitсинтаксис забезпечує краще рішення цього питання, не потребуючи додаткових бібліотек suspend. Дивіться цю відповідь для прикладу: stackoverflow.com/a/41957152/376789 . Функції асинхронізації доступні (без прапорців) з версії 7.10.
jmar777

20

У Linux / nodejs це працює для мене:

const spawnSync = вимагати ('child_process'). spawnSync;

var sleep = spawnSync ('сон', [1.5]);

Він блокує, але це не зайнятий цикл очікування.

Час, який ви вказуєте, становить секунди, але може бути часткою. Я не знаю, чи мають інші ОС подібні команди.


sleepє майже всюдисущим і корисним для початку з UNIX днів en.wikipedia.org/wiki/Sleep_%28Unix%29 . Тільки «не рідкість» ОС було б , можливо , не існує, вікно (проте ви можете спробуватиchild_process.spawnSync('timeout', ['/T', '10'])
humanityANDpeace

17

Якщо ви хочете "кодувати гольф", ви можете зробити скорочену версію деяких інших відповідей тут:

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

Але дійсно ідеальною відповіддю, на мою думку, є використання utilбібліотеки Node та її promisifyфункції, яка призначена саме для такого роду речей (виготовлення на основі обіцянок версій раніше існуючих необіцяних речей):

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

У будь-якому випадку ви можете зробити паузу просто, використовуючи awaitдля виклику своєї sleepфункції:

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

Насправді, використовуючи util.promisify, неможливо викликати сплячий режим, не надаючи також зворотного дзвінка. nodejs.org/api/…
Howie

1
Вам не вистачає await. Це на зразок створює зворотний виклик, призупиняє роботи, а потім перезавантажує код, коли цей зворотний виклик повертається. Зворотний виклик повертається зі значенням, але нас це не хвилює в цьому випадку, тому зліва немає нічого await.
machineghost

1
запишіть його в один рядок, щоб випити код гольфу;) const sleep = вимагати ('util'). Promify (setTimeout);
Фабіан

14

Нещодавно я створив більш просту абстракцію під назвою wait.for для виклику функцій асинхронізації в режимі синхронізації (на основі волокон вузла). Існує також версія, заснована на майбутніх генераторах ES6.

https://github.com/luciotato/waitfor

Використовуючи wait.for , ви можете викликати будь-яку стандартну функцію асинхронізації nodejs, як би функцію синхронізації, не блокуючи цикл подій вузла.

Ви можете кодувати послідовно, коли вам це потрібно, що ідеально підходить для спрощення сценаріїв для особистого використання.

за допомогою wait.for вашого коду буде:

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. 

Також будь-яку функцію асинхронізації можна викликати в режимі синхронізації . Перевірте приклади.


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

коментар говорить: "// визначено в waitfor / paralell-testing.js" захопіть його з цього файлу.
Луціо М. Тато

2
після того, як я отримав його, wait.for/paralell-tests.jsя зіткнувся з черговою помилкою, пов’язаною з невизначеними властивостями і т. д. Тому мені потрібно було також скопіювати їх. Чому ви не впорядкуєте код таким чином, що цього не потрібно?
користувач907860

Wait.for та інші рішення з волокон відкрили для мене цілий новий світ! Я б проголосував це мільйон разів, якби міг. Хоча більша частина спільноти nodejs виступає проти волокон, я вважаю, що вони є фантастичним доповненням і, безумовно, мають своє місце, коли справа доходить до пекла зворотного виклику.
Леві Робертс

7

Оскільки двигун javascript (v8) запускає код на основі послідовності подій у черзі подій, немає суворого того, що JavaScript точно запускає виконання через вказаний час. Тобто, якщо ви встановите кілька секунд для виконання коду пізніше, код запуску є суто основою на послідовності в черзі подій. Отже, запуску виконання коду може зайняти більше, ніж зазначений час.

Отже, Node.js випливає,

process.nextTick()

запустити код пізніше, а не setTimeout (). Наприклад,

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

2

Завдяки підтримці ES6 Promise, ми можемо використовувати їх без сторонньої допомоги.

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.

Потім використовуйте його так:

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

Зауважте, що doWhatфункція стає resolveзворотним викликом у межах new Promise(...).

Також зауважте, що це АСИНХРОННИЙ сон. Він не блокує цикл подій. Якщо вам потрібно блокувати сон, використовуйте цю бібліотеку, яка реалізує блокування сну за допомогою прив'язок C ++. (Хоча потреба у блокуванні сну в середовищах Node, як-от асинхронна, рідкісна.)

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

Цей код вище є побічним ефектом вирішення проблеми асинхронного зворотного виклику Javascript. Це також причина, на яку я думаю, що робить Javascript корисною мовою в бекенді. Насправді це найбільш хвилююче вдосконалення, представлене сучасним Javascript на мою думку. Щоб повністю зрозуміти, як він працює, як працює генератор, потрібно повністю зрозуміти. functionКлючове слово супроводжується *називається функцією генератора в сучасних JavaScript. Пакет npm coзабезпечував функцію бігуна для запуску генератора.

По суті, функція генератора забезпечувала спосіб призупинення виконання функції з yieldключовим словом, в той же час yieldу функції генератора давали можливість обмінюватися інформацією між генератором і абонентом. Це забезпечило механізм для абонента витягувати дані з promiseасинхронного виклику та передавати вирішені дані назад в генератор. Ефективно, він робить асинхронний дзвінок синхронним.


Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, як та / або чому він вирішує проблему, покращить довгострокове значення відповіді.
Дональд Дак

Дякуємо @DonaldDuck. Код у моїй відповіді, мабуть, є найбільш захоплюючою частиною вдосконалення Javascript. Я настільки вражений, що деякі супер розумні люди думають про такий спосіб вирішити проблему пекла зворотного дзвінка.
Qian Chen

1

Це модуль з ароматом moment.js, заснований на підході брудного блокування, запропонованому @ atlex2 . Використовуйте це лише для тестування .

const moment = require('moment');

let sleep = (secondsToSleep = 1) => {
    let sleepUntill = moment().add(secondsToSleep, 'seconds');
    while(moment().isBefore(sleepUntill)) { /* block the process */ }
}

module.exports = sleep;

0

Якщо вам потрібно просто призупинити для тестування цілі, ви виконуєте поточне виконання потоку, спробуйте це:

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

просто ми будемо чекати 5 секунд, щоб відбулася якась подія (про що буде позначено виконаною змінною, встановленою на істину десь в коді) або коли закінчиться час очікування, ми перевірятимемо кожні 100 мс

    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

Для деяких людей прийнята відповідь не працює, я знайшов цю іншу відповідь, і вона працює для мене: Як я можу передати параметр зворотному виклику setTimeout ()?

var hello = "Hello World";
setTimeout(alert, 1000, hello); 

'привіт' - це параметр, який передається, ви можете передати всі параметри після закінчення часу. Дякую @Fabio Phms за відповідь.


0

Замість того, щоб робити всі ці setTimeout, сон та багато інших речей, краще використовувати

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

Чи можете ви пояснити, що таке "затримка", і посилання на його документи? Чому краще?
хлопець

-2

Інші відповіді чудові, але я думав, що прийму інший такт.

Якщо все, що ви справді шукаєте, - це сповільнити певний файл в Linux:

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

вузол myprog slowfile

Це спатиме 1 сек кожні п’ять рядків. Програма вузла буде йти так само повільно, як і авторка. Якщо це робити інші речі, вони продовжуватимуться з нормальною швидкістю.

Mkfifo створює трубу "перший-в-перший". Саме це змушує цю роботу. Рядок Perl буде писати так швидко, як вам потрібно. $ | = 1 говорить, що не буфер виводу.


-3

Після прочитання відповідей у ​​цьому питанні я зібрав просту функцію, яка також може робити зворотний виклик, якщо вам це потрібно:

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

-4

Для отримання додаткової інформації про

yield sleep(2000); 

ви повинні перевірити Redux-Saga . Але це специфічно для вашого вибору Redux як вашої моделі моделі (хоча суворо не потрібно).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.