Как я могу ждать в Node.js (JavaScript)? Мне нужно сделать паузу на некоторое время

386

Я разрабатываю консольный скрипт для личных нужд. Я должен быть в состоянии сделать паузу в течение длительного периода времени, но, согласно моим исследованиям, 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');
    // end of code

Я также видел такие вещи, как

    yield sleep(2000);

Но Node.js не распознает это.

Как мне добиться этой продолжительной паузы?

Кристофер Аллен
источник
3
как насчет того, чтобы вы отметили одного из этих людей как правильный ответ? :)
Billybonks,
1
@ Кристофер Аллен, Может быть, не имеет значения, но делает работу: require("child_process").execSync('php -r "sleep($argv[1]);" ' + seconds);
Хайтам Свилем
Модуль node-sleep npm мог бы сработать (однако я бы использовал его только для отладки)
Джулиан Соро,

Ответы:

212

Лучший способ сделать это - разбить ваш код на несколько функций, например:

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-Дорис Что?
AturSams
3
используйте ответ @ machineghost для элегантного решения, основанного на обещаниях, которое не требует специального кода и поддерживает await / async
Дейн Маколей
Это асинхронно, это означает, что если функция1 завершается до 3 секунд, они начинают перекрывать друг друга. Тогда ты даже не сможешь вернуться, и его почти никогда не использовать. Единственный способ , которым я нашел до сих пор используюdebugger;
Cristian Traiña
429

Новый ответ на старый вопрос. Сегодня ( январь 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 . Вы можете использовать его сегодня с помощью веб-пакета 5 (альфа),

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

Аминадав Гликштейн
источник
5
Я думаю, что вы забыли awaitключевое слово передsleep(1000)
Райан Джарвис
6
Это действительно должен быть ответ, пакет sleep node.js может быть проблематичным.
Стюарт Аллен
4
Лучшее решение действительно
Костас Сиабанис
40
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));Для странных фанатов, как я :)
Предраг Стоядинович
21
let sleep = require('util').promisify(setTimeout);работает на узле 7.6+ и улучшает читаемость
BrianHVB
218

Самое короткое решение без каких-либо зависимостей:

await new Promise(resolve => setTimeout(resolve, 5000));
k06a
источник
14
«Высота утонченности - это простота». - Клэр Бут Люс. Это, безусловно, лучший ответ, ИМО.
Арнольд
3
Это, очевидно, гораздо более недавно, чем другие ответы, но это самый элегантный с 2018 года, который выполняет работу в 1 строку без каких-либо других воздействий на код.
Херик
1
Это хороший. Вы должны использовать NodeJS 7.6.0 или выше, хотя. Это не будет работать на старых версиях.
Райан Шиллингтон
5
let sleep = require('util').promisify(setTimeout);на три символа длиннее, но многоразового использования и более читабельного imo
BrianHVB
2
Чтобы избежать eslint жалобы, назвать его 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);
JohnnyHK
источник
2
Это ужасно грязно и обычно плохая практика, особенно если ОП хочет, чтобы остальная часть программы запустилась после этой задержки. Смотри мой ответ.
Эллиот Бонневиль
32
@ElliotBonneville Это просто пример, иллюстрирующий концепцию. Очевидно, что вы могли бы (должны) включить код в вызов функции вместо использования встроенного кода, как и везде.
JohnnyHK
@ChristopherKemp: Оказывается, Node.js предлагает решение для этого node-fibers. Проверьте это.
Эллиот Бонневиль
Это отличное решение, особенно если вы не хотите выполнять какой-либо код, вы просто хотите имитировать «спящий» метод внутри цикла. Ничего плохого в этом примере.
Halfstop
это идеально подходит для задержки запросов, поступающих от клиента, я использовал этот подход для тестирования своих загрузчиков на стороне клиента
moein rahimi
145

Это простая техника блокировки:

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

Он блокируется, поскольку в вашем скрипте больше ничего не произойдет (например, обратные вызовы). Но так как это консольный скрипт, возможно, это то, что вам нужно!

atlex2
источник
27
ужасно или нет, это просто wait, это блокирует, и это работает для некоторой цели тестирования. Именно то, что я искал.
Клийстерс
8
отлично отвечает на вопрос без сторонних библиотек и прост. Тем не менее, люди говорят «ужасно» .... Это здорово, например, для имитации большой загрузки процессора и т. Д. Кстати, очень похоже на этот phpied.com/sleep-in-javascript
Дон Чидл
2
Также это обязательный подход, если вы пытаетесь смоделировать что-то, что замедляет цикл обработки событий, тем самым замедляя других пользователей / соединений. Добавление отложенных обратных вызовов не повлияет на других пользователей / соединения.
Дон Чидл
13
Это spin-wait.
Майк Атлас
7
@ Али, похоже, цель.
Феликс Ганьон-Гренье
63

На узле 7.6.0 или выше

Node поддерживает ожидание изначально:

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

На старых версиях Node (оригинальный ответ)

Я хотел асинхронный сон, который работал бы в 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. Оригинальный репо предупреждает не использовать его, поскольку это взломать. Он работает, но не на всех платформах, поэтому, если вам нужно кроссплатформенное решение, избегайте этого.
danday74
1
да, у меня никогда не было проб с этим, и это здорово для dev - под капотом я полагаю, что он опирается на C или C ++, который широко доступен, но я полагаю, что на некоторых машинах это не так и то, когда он терпит неудачу, поэтому он вызывает проблемы - если это не удается, системный сон возвращается к ожиданию вращения, которое
останавливает
1
Этот пакет не работает в Mac OS, и поэтому не является кросс-совместимым и поэтому не работает. См github.com/jochemstoel/nodejs-system-sleep/issues/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
Aleksej
источник
4
Он работает нормально MacOS, но он сталкивается с ошибками CentOSиз-за ошибок node_gyp . Кажется, не портативный.
AechoLiu
1
возможно проблема вызвана не ОС, а сборкой узла
Алексей
34

Этот вопрос довольно старый, но недавно V8 добавил генераторы, которые могут выполнять то, что запрашивал OP. Генераторы, как правило, проще всего использовать для асинхронных взаимодействий с помощью библиотеки, такой как suspend или gen-run .

Вот пример использования suspend:

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

Связанное чтение (путем бесстыдного саморекламы): какова большая сделка с генераторами? ,

jmar777
источник
2
Хороший ответ - но стоит прочитатьyield setTimeout(suspend.resume(), 10000);
edhubbell
1
Спасибо, @edhubbell. Этот ответ был основан на очень старой версии Suspend, но вы правы относительно последней. Я обновлю ответ.
jmar777
для какой версии узла это?
danday74
1
@ danday74 Я не могу точно вспомнить, когда они не были помечены, но они существуют с 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 = require ('child_process'). spawnSync;

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

Это блокировка, но это не занятый цикл ожидания.

Указанное вами время указывается в секундах, но может быть и долей. Я не знаю, есть ли в других ОС аналогичная команда.

Барт Верхейен
источник
sleepв значительной степени вездесущий и полезный с ранних дней UNIX en.wikipedia.org/wiki/Sleep_%28Unix%29 . Только «не редкость», о которой он, возможно, не существовал бы, это окна (как бы там ни было, вы можете попытатьсяchild_process.spawnSync('timeout', ['/T', '10'])
человечествоANDpeace
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
machineghost
источник
На самом деле, используя util.promisify, невозможно вызвать сон без предоставления обратного вызова. nodejs.org/api/…
Хауи
1
Ты скучаешь по await. Это своего рода создает обратный вызов, приостанавливает работу, а затем перезапускает код при возврате этого обратного вызова. Обратный вызов возвращается со значением, но в данном случае мы не заботимся об этом, поэтому слева от него ничего нет await.
machineghost
1
напишите его в одну строку, чтобы получить код гольф;) const sleep = require ('util'). promisify (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'
Кофеин
комментарий гласит: "// определено в waitfor / paralell-tests.js", извлеките его из этого файла.
Лусио М. Тато
2
после того, как я его получил, wait.for/paralell-tests.jsя обнаружил еще одну ошибку, связанную с неопределенными свойствами и т. д. Поэтому мне нужно было их тоже скопировать. Почему бы вам не организовать код таким образом, чтобы это не требовалось?
user907860
Wait.for и другие оптоволоконные решения открыли для меня целый новый мир! Я бы проголосовал за это миллион раз, если бы мог. Хотя большинство сообщества nodejs выступают против волокон, я думаю, что они фантастическое дополнение и определенно имеют свое место, когда дело доходит до ада обратного вызова.
Леви Робертс
7

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

Итак, Node.js следует,

process.nextTick()

запустить код позже вместо setTimeout (). Например,

process.nextTick(function(){
    console.log("This will be printed later");
});
HILARUDEEN S ALLAUDEEN
источник
2

С ES6, поддерживающими Promises, мы можем использовать их без посторонней помощи.

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

treecoder
источник
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предоставляет функцию runner для запуска генератора.

По сути, функция генератора обеспечивает способ приостановить выполнение функции с yieldключевым словом, в то же время yieldфункция генератора позволяет обмениваться информацией между генератором и вызывающей стороной. Это предоставило вызывающему механизму механизм извлечения данных promiseиз асинхронного вызова и передачи разрешенных данных обратно в генератор. По сути, это делает асинхронный вызов синхронным.

Цянь Чен
источник
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшит долгосрочную ценность ответа.
Дональд Дак
Спасибо @DonaldDuck. Код в моем ответе, пожалуй, самая захватывающая часть улучшений в Javascript. Я так поражен, что некоторые очень умные люди думают об этом способе решить проблему ада с обратным вызовом.
Цянь Чен
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 секунд, чтобы произошло какое-то событие (на что указывает переменная done, установленная в true где-то еще в коде), или по истечении времени ожидания мы будем проверять каждые 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); 

'hello' - передаваемый параметр, вы можете передать все параметры по истечении времени ожидания. Спасибо @Fabio Phms за ответ.

Dazt
источник
0

Вместо того, чтобы делать все эти setTimeout, sleep и многое другое, лучше использовать

const delay = require('delay');
await delay(2000); // will wait for 2 seconds before executing next line of code
Rishab
источник
Можете ли вы объяснить, что такое «задержка», и дать ссылку на ее документы? Почему лучше?
бойси
-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
  }
}
Netsi1964
источник
-4

Для получения дополнительной информации о

yield sleep(2000); 

Вы должны проверить Redux-Saga . Но это зависит от вашего выбора Redux в качестве вашей модели фреймворка (хотя строго не обязательно).

Арсений Буйницкий
источник