Все, что вам нужно знать

1656536896 vse chto vam nuzhno znat

Несколько недель назад я написал в Твиттере этот вопрос об интервью:

*** Ответьте на вопросы в голове, прежде чем продолжить ***

Около половины ответов на твиты были неправильными. Ответ таков НЕТ V8 (или другие виртуальные машины)!! Хотя известно как «таймеры JavaScript», функционирует как setTimeout и setInterval не является частью спецификаций ECMAScript или каких-либо реализаций механизма JavaScript. Функции таймера реализуются браузерами, и их реализации будет отличаться в разных браузерах. Таймеры также реализованы в самой среде выполнения Node.js.

В браузерах основные функции таймера являются частью Window интерфейс, имеющий несколько других функций и объектов. Этот интерфейс делает все элементы доступными глобально в основной области JavaScript. Вот почему вы можете выполнить setTimeout непосредственно в консоли браузера.

В Node таймеры являются частью global объект, ведущий себя так же, как и браузер Window интерфейс. Вы можете увидеть исходный код таймеров в Node здесь.

Некоторые могут подумать, что это плохой вопрос на собеседовании — почему все-таки знание этого имеет значение?! Как разработчик JavaScript, я думаю, что вы должны знать это, потому что если вы этого не сделаете, это может являться признаком того, что вы не полностью понимаете, как V8 (и другие виртуальные машины) взаимодействуют с браузерами и Node.

Давайте приведем несколько примеров и задач по функциям таймера, не так ли?

Обновление: Эта статья теперь является частью моего «Полного знакомства с Node.js».
Вы можете прочитать его обновленную версию здесь.

Задержка выполнения функции

Функции таймера – это функции высшего порядка, которые можно использовать для задержки или повторения выполнения других функций (которые они получают в качестве своего первого аргумента).

Вот пример задержки:

// example1.js
setTimeout(
  () => {
    console.log('Hello after 4 seconds');
  },
  4 * 1000
);

В этом примере используется setTimeout чтобы отложить печать поздравительного сообщения на 4 секунды. Второй аргумент к setTimeout – задержка (в мс). Вот почему я умножил 4 на 1000, чтобы получить 4 секунды.

Первый аргумент к setTimeout это функция, исполнение которой будет отложено.

Если вы выполняете example1.js файл с node Команда Node приостановится на 4 секунды, а затем напечатает поздравительное сообщение (и выйдет после этого).

Обратите внимание, что первый аргумент к setTimeout это просто функция ссылка. Это не обязательно должна быть встроенная функция как what example1.js имеет. Вот тот же пример без использования встроенной функции:

const func = () => {
  console.log('Hello after 4 seconds');
};
setTimeout(func, 4 * 1000);

Переходные аргументы

Если функция, которая использует setTimeout чтобы отложить его исполнение принимает любые аргументы, мы можем использовать другие аргументы for setTimeout сам (после двух, о которых мы узнали до сих пор), чтобы передать значение аргументов в отложенную функцию.

// For: func(arg1, arg2, arg3, ...)
// We can use: setTimeout(func, delay, arg1, arg2, arg3, ...)

Вот пример:

// example2.js
const rocks = who => {
  console.log(who + ' rocks');
};
setTimeout(rocks, 2 * 1000, 'Node.js');

The rocks функция выше, задерживаемая на 2 секунды, принимает a who аргумент и setTimeout вызов реле значениеNode.js” как это who аргумент.

Выполнение example2.js с node команда распечатает «Node.js камни” через 2 секунды.

Задание таймеров №1

Используя то, что вы узнали setTimeoutнапечатайте следующие 2 сообщения после соответствующих задержек.

  • Распечатать сообщение «Привет через 4 секунды” через 4 секунды
  • Распечатать сообщение «Привет через 8 секунд” через 8 секунд.

Ограничение:
В своем решении можно определить только одну функцию, включающую в себя встроенные функции. Это означает много setTimeout вызовы должны использовать ту же функцию.

Решение

Вот как я решил бы эту задачу:

// solution1.js
const theOneFunc = delay => {
  console.log('Hello after ' + delay + ' seconds');
};
setTimeout(theOneFunc, 4 * 1000, 4);
setTimeout(theOneFunc, 8 * 1000, 8);

я сделал theOneFunc получить а delay аргумент и использовал значение that delay аргумент в печатном сообщении. Таким образом, функция может печатать различные сообщения на основе любого значения задержки, которое мы передаем ей.

Я тогда использовал theOneFunc в двоих setTimeout вызовы, один, который запускается через 4 секунды, а другой – через 8 секунд. Оба setTimeout звонки также получают a 3-й аргумент для представления delay аргумент за theOneFunc.

Выполнение solution1.js файл с node Команда распечатает требования к заданию первое сообщение через 4 секунды, а второе сообщение через 8 секунд.

Повторение выполнения функции

Что, если бы я попросил вас печатать сообщения каждые 4 секунды, навсегда?

Пока можно поставить setTimeout в цикле API таймеров предлагает setInterval а также функция, которая выполнила бы требование делать что-то вечно.

Вот пример setInterval:

// example3.js
setInterval(
  () => console.log('Hello every 3 seconds'),
  3000
);

Этот пример будет печатать сообщения каждые 3 секунды. Выполнение example3.js с node Команда заставит Node печатать это сообщение навсегда, пока вы не завершите процесс (with CTRL+C).

Отмена таймеров

Поскольку вызов функции таймера планирует действие, это действие можно отменить, прежде чем оно будет выполнено.

Звонок в setTimeout возвращает «ID» таймера, и вы можете использовать этот идентификатор таймера с a clearTimeout позвоните, чтобы отменить этот таймер. Вот пример:

// example4.js
const timerId = setTimeout(
  () => console.log('You will not see this one!'),
  0
);
clearTimeout(timerId);

Этот простой таймер должен сработать после 0 ms (что делает это мгновенным), но этого не будет, поскольку мы захватываем timerId значение и отменяя его сразу после этого с помощью a clearTimeout звонить.

Когда мы выполняем example4.js с node Команда Node ничего не будет печатать и процесс просто завершится.

Кстати, у Node.js есть еще один способ setTimeout с 0 РС. API таймера Node.js имеет еще одну функцию setImmediateи это в основном то же, что a setTimeout с 0 мс, но нам не нужно указывать там задержку:

setImmediate(
  () => console.log('I am equivalent to setTimeout with 0 ms'),
);

The setImmediate функция доступна не во всех браузерах. Не используйте его для кода интерфейса.

Так как clearTimeoutтакже есть a clearInterval функция, которая выполняет то же самое, но для setInerval звонков, а также есть a clearImmediate также звоните.

Задержка таймера не является гарантированной вещью

В предыдущем примере вы заметили, как выполняется что-то с помощью setTimeout после 0 ms не означало ли выполнить его сразу (после строки setTimeout), а скорее выполнить его сразу после всего остального в сценарии (включая вызов clearTimeout)?

Позвольте мне это объяснить на примере. Вот просто setTimeout вызов, который должен сработать через полсекунды, но не будет:

// example5.js
setTimeout(
  () => console.log('Hello after 0.5 seconds. MAYBE!'),
  500,
);
for (let i = 0; i < 1e10; i++) {
  // Block Things Synchronously
}

Сразу после определения таймера в этом примере мы блокируем время выполнения синхронно с big for петля. The 1e10 есть 1 с 10 нули перед ним, следовательно, цикл равен a 10 Миллиардный цикл (что в основном имитирует занятый процессор). Узел ничего не может делать, пока этот цикл работает.

На практике это, конечно, очень плохо, но это поможет вам понять это setTimeout задержка не является гарантированной вещью, а скорее минимум вещь. The 500 мс означает минимальную задержку 500 РС. На самом деле, сценарию понадобится гораздо больше времени, чтобы напечатать строку приветствия. Придется подождать, пока цикл блокировки завершится первым.

Задание таймеров №2

Напишите скрипт для печати сообщения «Привет Мир” каждую секунду, но только 5 раз. После 5 раз сценарий должен напечатать сообщение.Готово” и разрешите процессу Node завершить работу.

Ограничение: Вы не можете использовать a setTimeout призывать к этому вызову.
Подсказка: Вам нужен счетчик

Решение

Вот как я решил бы этот вопрос:

let counter = 0;
const intervalId = setInterval(() => {
  console.log('Hello World');
  counter += 1;
if (counter === 5) {
    console.log('Done');
    clearInterval(intervalId);
  }
}, 1000);

Я инициировал а counter значение как 0 а затем начал а setInterval вызов, увлекая его идентификатор.

Функция задержки будет печатать сообщения и увеличивать счетчик. Внутри отложенной функции, an if заявка проверит, мы ли на 5 раз. Если да, то будет напечатано «Готово” и очистите интервал с помощью захваченного intervalId постоянный. Интервальная задержка составляет 1000 РС.

Кто именно вызывает отложенные функции?

Когда вы используете JavaScript this ключевое слово внутри обычной функции, например:

function whoCalledMe() {
  console.log('Caller is', this);
}

Значение внутри this ключевое слово будет представлять абонента функции. Если вы определите функцию выше внутри узла REPL, вызов будет global объект. Если вы определите функцию в консоли обозревателя, абонентом будет window объект.

Давайте определим функцию как свойство объекта, чтобы сделать это более понятным:

const obj = { 
  id: '42',
  whoCalledMe() {
    console.log('Caller is', this);
  }
};
// The function reference is now: obj.whoCallMe

Теперь, когда вы позвоните obj.whoCallMe функцией, используя ее ссылку непосредственно, вызов будет obj объект (идентифицированный его идентификатором):

1*oo6w6C8omvxjxSwK_FJsag

Теперь вопрос состоит в том, каким будет вызов, если мы передаем ссылку obj.whoCallMe к а setTimetout звонить?

// What will this print??
setTimeout(obj.whoCalledMe, 0);

Кто будет в этом случае абонентом?

Ответ отличается в зависимости от того, где выполняется функция таймера. В этом случае вы просто не можете зависеть от звонящего. Вы теряете контроль над вызывающим, потому что реализация таймера будет тот, что вызовет вашу функцию сейчас. Если вы протестируете его в Node REPL, вы получите a Timetout объект как вызывает:

1*du_RKr4vPNh1irFRPR92EA

Обратите внимание, что это имеет значение, только если вы используете JavaScript this ключевое слово внутри обычных функций. Вам не нужно беспокоиться об абоненте, если вы используете функции со стрелками.

Задание таймеров №3

Напишите сценарий для непрерывной печати сообщения «Привет Мир” с разными задержками. Начните с задержкой в ​​1 секунду, а затем увеличивайте задержку каждый раз на 1 секунду. Второй раз будет иметь задержку 2 секунды. Третий раз будет задержка 3 секунды и так далее.

Включите задержку в распечатанном сообщении. Ожидаемый выход выглядит так:

Hello World. 1
Hello World. 2
Hello World. 3
...

Ограничение: Вы можете использовать только const чтобы определить переменные. Вы не можете использовать let или var.

Решение

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

Кроме того, поскольку мы не можем использовать let/var, мы не можем иметь счетчик для увеличения задержки при каждом рекурсивном вызове, но вместо этого мы можем использовать аргументы рекурсивной функции для увеличения при рекурсивном вызове.

Вот один из возможных способов решения этой проблемы:

const greeting = delay =>
  setTimeout(() => {
    console.log('Hello World. ' + delay);
    greeting(delay + 1);
  }, delay * 1000);
greeting(1);

Задача таймеров №4

Напишите сценарий для непрерывной печати сообщения «Привет Мир” с такой же концепцией переменных задержек, что и задача №3, но на этот раз в группах по 5 сообщений на интервал основной задержки. Начиная с задержкой 100 мс для первых 5 сообщений, затем задержка 200 мс для следующих 5 сообщений, затем 300 мс и так далее.

Вот как должен вести себя скрипт:

  • Через 100 мс сценарий начнет печатать Hello World и сделает это 5 раз с интервалом 100 мс. Первое сообщение появится через 100 мс, 2 сообщения – через 200 мс.
  • После первых 5 сообщений, сценарий должен увеличить основную задержку до 200 мс. Итак, 6-е сообщение будет напечатано за 500 мс + 200 мс (700 мс), 7-е сообщение будет напечатано за 900 мс, 8-е сообщение будет напечатано за 1100 мс и так далее.
  • После 10 сообщений, сценарий должен увеличить основную задержку до 300 мс. Следовательно, 11-е сообщение должно быть опубликовано за 500 мс + 1000 мс + 300 мс (18 000 мс). 12-е сообщение должно быть напечатано за 21000 мс.
  • Продолжайте шаблон навсегда.

Включите задержку в распечатанном сообщении. Ожидаемый результат выглядит следующим образом (без комментариев):

Hello World. 100  // At 100ms
Hello World. 100  // At 200ms
Hello World. 100  // At 300ms
Hello World. 100  // At 400ms
Hello World. 100  // At 500ms
Hello World. 200  // At 700ms
Hello World. 200  // At 900ms
Hello World. 200  // At 1100ms
...

Ограничение: Вы можете использовать только setInterval звонки (нет setTimeout), и вы можете использовать только ОДИН оператор if.

Решение

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

Вот одно из возможных решений:

let lastIntervalId, counter = 5;
const greeting = delay => {
  if (counter === 5) {
    clearInterval(lastIntervalId);
    lastIntervalId = setInterval(() => {
      console.log('Hello World. ', delay);
      greeting(delay + 100);
    }, delay);
    counter = 0;
  }
counter += 1;
};
greeting(100);

Спасибо, что прочли.

Если вы только начинаете учить Node.js, я недавно опубликовал файл курс первых шагов в PluralsightПроверь:

1*OoRpYXrRivoSnQTscAjCMw

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *