From 980fe00a74a9ad013b945755415ace2e5429c3c2 Mon Sep 17 00:00:00 2001 From: Alexey Pyltsyn Date: Wed, 27 Oct 2021 02:31:24 +0300 Subject: [RU] Remove notranslate (#2874) --- .../javascript/asynchronous/async_await/index.html | 44 +++++++-------- .../javascript/asynchronous/concepts/index.html | 18 +++--- .../javascript/asynchronous/introducing/index.html | 22 ++++---- .../asynchronous/timeouts_and_intervals/index.html | 64 +++++++++++----------- 4 files changed, 74 insertions(+), 74 deletions(-) (limited to 'files/ru/learn/javascript/asynchronous') diff --git a/files/ru/learn/javascript/asynchronous/async_await/index.html b/files/ru/learn/javascript/asynchronous/async_await/index.html index 7280fe9843..e64c9cc30b 100644 --- a/files/ru/learn/javascript/asynchronous/async_await/index.html +++ b/files/ru/learn/javascript/asynchronous/async_await/index.html @@ -36,36 +36,36 @@ translation_of: Learn/JavaScript/Asynchronous/Async_await

Попробуйте выполнить в консоли браузера следующий код:

-
function hello() { return "Hello" };
+
function hello() { return "Hello" };
 hello();

Функция возвращает "Hello" — ничего необычного, верно ?

Но что если мы сделаем её асинхронной ? Проверим:

-
async function hello() { return "Hello" };
+
async function hello() { return "Hello" };
 hello();

Как было сказано ранее, вызов асинхронной функции возвращает объект Promise.

Вот пример с async function expression:

-
let hello = async function() { return "Hello" };
+
let hello = async function() { return "Hello" };
 hello();

Также можно использовать стрелочные функции:

-
let hello = async () => { return "Hello" };
+
let hello = async () => { return "Hello" };

Все они в общем случае делают одно и то же.

Чтобы получить значение, которое возвращает Promise, мы как обычно можем использовать метод .then():

-
hello().then((value) => console.log(value))
+
hello().then((value) => console.log(value))

или ещё короче

-
hello().then(console.log)
+
hello().then(console.log)
 

Итак, ключевое слово async, превращает обычную функцию в асинхронную и результат вызова функции оборачивает в Promise. Также асинхронная функция позволяет использовать в своём теле ключевое слово await, о котором далее.

@@ -78,7 +78,7 @@ hello();

Небольшой пример:

-
async function hello() {
+
async function hello() {
   return greeting = await Promise.resolve("Hello");
 };
 
@@ -90,7 +90,7 @@ hello().then(alert);

Давайте посмотрим на пример из предыдущей статьи:

-
fetch('coffee.jpg')
+
fetch('coffee.jpg')
 .then(response => {
   if (!response.ok) {
     throw new Error(`HTTP error! status: ${response.status}`);
@@ -110,7 +110,7 @@ hello().then(alert);

К этому моменту вы должны понимать как работают Promises, чтобы понять все остальное. Давайте перепишем код используя async/await и оценим разницу.

-
async function myFetch() {
+
async function myFetch() {
   let response = await fetch('coffee.jpg');
 
   if (!response.ok) {
@@ -134,7 +134,7 @@ myFetch()
 
 

Так как ключевое слово async заставляет функцию вернуть Promise, мы можем использовать гибридный подход:

-
async function myFetch() {
+
async function myFetch() {
   let response = await fetch('coffee.jpg');
   if (!response.ok) {
     throw new Error(`HTTP error! status: ${response.status}`);
@@ -160,7 +160,7 @@ myFetch().then((blob) => {
  
Пример:

-
let response = await fetch('coffee.jpg');
+
let response = await fetch('coffee.jpg');

Значение Promise, которое вернёт fetch() будет присвоено переменной response только тогда, когда оно будет доступно - парсер делает паузу на данной строке дожидаясь этого момента. Как только значение доступно, парсер переходит к следующей строке, в которой создаётся объект Blob из результата Promise. В этой строке, кстати, также используется await, потому что метод .blob() также возвращает Promise. Когда результат готов, мы возвращаем его наружу из myFetch().

@@ -175,7 +175,7 @@ myFetch().then((blob) => {

Мы можем использовать синхронную try...catch структуру с async/await. Вот изменённая версия первого примера выше:

-
async function myFetch() {
+
async function myFetch() {
   try {
     let response = await fetch('coffee.jpg');
 
@@ -199,7 +199,7 @@ myFetch();

Если вы хотите использовать гибридный подходы (пример выше), лучше использовать блок .catch() после блока .then() вот так:

-
async function myFetch() {
+
async function myFetch() {
   let response = await fetch('coffee.jpg');
   if (!response.ok) {
     throw new Error(`HTTP error! status: ${response.status}`);
@@ -233,7 +233,7 @@ myFetch().then((blob) => {
 
 

Версия с async/await (смотрите live demo и source code), сейчас выглядит так:

-
async function fetchAndDecode(url, type) {
+
async function fetchAndDecode(url, type) {
   let response = await fetch(url);
 
   let content;
@@ -282,7 +282,7 @@ displayContent()
 
 

Вы видите, что мы легко изменили fetchAndDecode() функцию в асинхронный вариант. Взгляните на строку с Promise.all():

-
let values = await Promise.all([coffee, tea, description]);
+
let values = await Promise.all([coffee, tea, description]);

С помощью await мы ждём массив результатов всех трёх Promises и присваиваем его в переменную values. Это асинхронный код, но он написан в синхронном стиле, за счёт чего он гораздо читабельнее.

@@ -306,7 +306,7 @@ displayContent()

Мы подготовили два примера  — slow-async-await.html (см. source code) и fast-async-await.html (см. source code). Они оба начинаются с функции возвращающей promise, имитирующей асинхронность процессов при помощи вызова setTimeout():

-
function timeoutPromise(interval) {
+
function timeoutPromise(interval) {
   return new Promise((resolve, reject) => {
     setTimeout(function(){
       resolve("done");
@@ -316,13 +316,13 @@ displayContent()
 
 

Далее в каждом примере есть асинхронная функция  timeTest() ожидающая три вызова timeoutPromise():

-
async function timeTest() {
+
async function timeTest() {
   ...
 }

В каждом примере функция записывает время начала исполнения и сколько времени понадобилось на исполнение  timeTest()  промисов, вычитая время в момент запуска функции из времени в момент разрешения промисов:

-
let startTime = Date.now();
+
let startTime = Date.now();
 timeTest().then(() => {
   let finishTime = Date.now();
   let timeTaken = finishTime - startTime;
@@ -333,7 +333,7 @@ timeTest().then(() => {
 
 

В случае с медленным примером slow-async-await.html, timeTest() выглядит:

-
async function timeTest() {
+
async function timeTest() {
   await timeoutPromise(3000);
   await timeoutPromise(3000);
   await timeoutPromise(3000);
@@ -343,7 +343,7 @@ timeTest().then(() => {
 
 

Во втором  fast-async-await.html примере, функция timeTest() выглядит как:

-
async function timeTest() {
+
async function timeTest() {
   const timeoutPromise1 = timeoutPromise(3000);
   const timeoutPromise2 = timeoutPromise(3000);
   const timeoutPromise3 = timeoutPromise(3000);
@@ -365,7 +365,7 @@ timeTest().then(() => {
 
 

В качестве последнего замечания, вы можете использовать  async  перед методами классов или объектов, вынуждая их возвращать promises. А также  await внутри методов объявленных таким образом. Посмотрите на пример ES class code, который мы наблюдали в статье  object-oriented JavaScript,  и сравните его с модифицированной (асинхронной) async версией ниже:

-
class Person {
+
class Person {
   constructor(first, last, age, gender, interests) {
     this.name = {
       first,
@@ -389,7 +389,7 @@ let han = new Person('Han', 'Solo', 25, 'male', ['Smuggling']);

Первый метод класса теперь можно использовать таким образом:

-
han.greeting().then(console.log);
+
han.greeting().then(console.log);

Browser support (Поддержка браузерами)

diff --git a/files/ru/learn/javascript/asynchronous/concepts/index.html b/files/ru/learn/javascript/asynchronous/concepts/index.html index bdc84b6f72..fe4e6c7343 100644 --- a/files/ru/learn/javascript/asynchronous/concepts/index.html +++ b/files/ru/learn/javascript/asynchronous/concepts/index.html @@ -45,7 +45,7 @@ translation_of: Learn/JavaScript/Asynchronous/Concepts

В нашем simple-sync.html примере (see it running live), добавим кнопке событие на клик, чтобы при нажатии на неё запускалась трудоёмкая операция (расчёт 10000000 дат, и вывод последней рассчитанной даты на консоль) после чего в DOM добавляется ещё один параграф:

-
const btn = document.querySelector('button');
+
const btn = document.querySelector('button');
 btn.addEventListener('click', () => {
   let myDate;
   for(let i = 0; i < 10000000; i++) {
@@ -75,7 +75,7 @@ btn.addEventListener('click', () => {
  
  • Кнопка "Click me for alert", при нажатии показывает предупреждение.
  • -
    function expensiveOperation() {
    +
    function expensiveOperation() {
       for(let i = 0; i < 1000000; i++) {
         ctx.fillStyle = 'rgba(0,0,255, 0.2)';
         ctx.beginPath();
    @@ -102,24 +102,24 @@ alertBtn.addEventListener('click', () =>
     
     

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

    -
    Task A --> Task B --> Task C
    +
    Task A --> Task B --> Task C

    Каждая задача будет выполнена последовательно; только когда текущая задача завершится, следующая сможет начаться.

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

    -
    Thread 1: Task A --> Task B
    +
    Thread 1: Task A --> Task B
     Thread 2: Task C --> Task D

    JavaScript однопоточный

    JavaScript, традиционно для скриптовых языков, однопоточный. Даже, если есть несколько ядер, вы можете использовать их только для выполнения задач в одном потоке, называемом основной поток. Наш пример выше, выполняется следующим образом:

    -
    Main thread: Render circles to canvas --> Display alert()
    +
    Main thread: Render circles to canvas --> Display alert()

    В итоге, JavaScript получил несколько инструментов, которые могут помочь в решении подобных проблем. Web workers позволяют вам обработать некоторый JavaScript-код в отдельном потоке, который называется обработчик, таким образом вы можете запускать отдельные блоки JavaScript-кода одновременно. В основном, вы будете использовать воркеры, чтобы запустить ресурсоёмкий процесс, отдельно от основного потока, чтобы не блокировать действия пользователя.

    -
      Main thread: Task A --> Task C
    +
      Main thread: Task A --> Task C
     Worker thread: Expensive task B

    Помня об этом, выполните наш следующий пример simple-sync-worker.html (посмотреть пример в действии), с открытой консолью. Это переписанный предыдущий пример, который теперь рассчитывает 10 миллионов дат в отдельном потоке обработчика. Теперь, когда вы нажимаете на кнопку, браузер может добавить новый элемент на страницу, до того как все даты будут посчитаны. Самая первая операция больше не блокирует выполнение следующей.

    @@ -130,18 +130,18 @@ Worker thread: Expensive task B

    Следующая проблема заключается в том, что даже если код запущенный в воркере ничего не блокирует, он в целом остаётся синхронным. Это проблема появляется, когда какой-то функции требуются результаты выполнения нескольких предыдущих функций. Рассмотрим следующую диаграмму потоков:

    -
    Main thread: Task A --> Task B
    +
    Main thread: Task A --> Task B

    В этом примере, предположим Task A делает что-то вроде получения картинки с сервера а Task B затем делает что-нибудь с полученной картинкой, например, применяет к ней фильтр. Если запустить выполняться Task A и тут же попытаться выполнить Task B, то вы получите ошибку, поскольку картинка ещё не будет доступна.

    -
      Main thread: Task A --> Task B --> |Task D|
    +
      Main thread: Task A --> Task B --> |Task D|
     Worker thread: Task C -----------> |      |

    Теперь, давайте предположим, что Task D использует результат выполнения обеих задач Task B и Task C. Если мы уверенны, что оба результата будут доступны одновременно, тогда не возникнет проблем, однако, часто это не так. Если Task D попытаться запустить, когда какого-то нужного ей результата ещё нет, выполнение закончится ошибкой.

    Чтобы избежать подобных проблем, браузеры позволяют нам выполнять определённые операции асинхронно. Такие возможности, как Promises позволяют запустить некоторую операцию (например, получение картинки с сервера), и затем подождать пока операция не вернёт результат, перед тем как начать выполнение другой задачи:

    -
    Main thread: Task A                   Task B
    +
    Main thread: Task A                   Task B
         Promise:      |__async operation__|

    Поскольку операция выполняется где-то отдельно, основной поток не блокируется, при выполнении асинхронных задач.

    diff --git a/files/ru/learn/javascript/asynchronous/introducing/index.html b/files/ru/learn/javascript/asynchronous/introducing/index.html index 2d457b6888..0b53770834 100644 --- a/files/ru/learn/javascript/asynchronous/introducing/index.html +++ b/files/ru/learn/javascript/asynchronous/introducing/index.html @@ -36,7 +36,7 @@ translation_of: Learn/JavaScript/Asynchronous/Introducing

    Большая часть функциональности, которую мы рассматривали в предыдущих обучающих модулях, является синхронной — вы запускаете какой-то код, а результат возвращается, как только браузер может его вернуть. Давайте рассмотрим простой пример ( посмотрите онлайн, как это работает и посмотрите исходный код):

    -
    const btn = document.querySelector('button');
    +
    const btn = document.querySelector('button');
     btn.addEventListener('click', () => {
       alert('You clicked me!');
     
    @@ -65,7 +65,7 @@ btn.addEventListener('click', () => {
     

    Так и в примере выше: после нажатия кнопки абзац не сможет появиться пока не будет нажата кнопка OK в окне сообщения. Попробуйте сами:

    {{EmbedLiveSample('Синхронный_JavaScript', '100%', '70px')}}

    @@ -80,7 +80,7 @@ btn.addEventListener('click', () => {

    Почему трудно работать, используя синхронный код? Давайте посмотрим на небольшой пример. Когда вы получаете картинку с сервера, вы не можете мгновенно вернуть результат. Это значит что следующий (псевдо) код не сработает:

    -
    let response = fetch('myImage.png');
    +
    let response = fetch('myImage.png');
     let blob = response.blob();
     // display your image blob in the UI somehow
    @@ -94,7 +94,7 @@ let blob = response.blob();

    Пример асинхронного колбэка вторым параметром {{domxref("EventTarget.addEventListener", "addEventListener()")}} (как мы видели выше):

    -
    btn.addEventListener('click', () => {
    +
    btn.addEventListener('click', () => {
       alert('You clicked me!');
     
       let pElem = document.createElement('p');
    @@ -108,7 +108,7 @@ let blob = response.blob();
     
     

    Вы можете написать свою собственную функцию, содержащую колбэк-функцию. Давайте взглянем на ещё один пример, в котором происходит загрузка ресурсов через XMLHttpRequest API (запустите пример, и посмотрите исходный код):

    -
    function loadAsset(url, type, callback) {
    +
    function loadAsset(url, type, callback) {
       let xhr = new XMLHttpRequest();
       xhr.open('GET', url);
       xhr.responseType = type;
    @@ -136,7 +136,7 @@ loadAsset('coffee.jpg', 'blob', displayImage);

    Заметьте, что не все колбэк-функции асинхронны — некоторые запускаются синхронно. Например, при использовании {{jsxref("Array.prototype.forEach()")}} для перебора элементов массива (запустите пример, и посмотрите исходный код):

    -
    const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];
    +
    const gods = ['Apollo', 'Artemis', 'Ares', 'Zeus'];
     
     gods.forEach(function (eachName, index){
       console.log(index + '. ' + eachName);
    @@ -148,7 +148,7 @@ gods.forEach(function (eachName, index){
     
     

    Промисы — новый стиль написания асинхронного кода, который используется в современных Web API. Хорошим примером является fetch() API, который современнее и эффективнее чем {{domxref("XMLHttpRequest")}}. Посмотрим на краткий пример, из нашей статьи Fetching data from the server:

    -
    fetch('products.json').then(function(response) {
    +
    fetch('products.json').then(function(response) {
       return response.json();
     }).then(function(json) {
       products = json;
    @@ -195,7 +195,7 @@ gods.forEach(function (eachName, index){
     
     

    Давайте рассмотрим пример, который дополнительно иллюстрирует природу асинхронного кода, показывая, что может произойти, когда мы не полностью осознаем порядок выполнения кода, и проблемы, связанные с попыткой трактовать асинхронный код как синхронный. Следующий пример довольно похож на тот, что мы видели раньше (запустите пример, и посмотреть исходный код). Одно из отличий состоит в том, что мы включили ряд операторов {{domxref("console.log()")}} чтобы проиллюстрировать порядок, в котором, как вы думаете, будет выполняться код.

    -
    console.log ('Starting');
    +
    console.log ('Starting');
     let image;
     
     fetch('coffee.jpg').then((response) => {
    @@ -226,7 +226,7 @@ console.log ('All done!');

    Если вы запутались, рассмотрим следующий небольшой пример:

    -
    console.log("registering click handler");
    +
    console.log("registering click handler");
     
     button.addEventListener('click', () => {
       console.log("get click");
    @@ -240,11 +240,11 @@ console.log("all done");

    Чтобы увидеть это в действии, попробуйте взять локальную копию нашего примера и измените третий вызов console.log () следующим образом:

    -
    console.log ('All done! ' + image + 'displayed.');
    +
    console.log ('All done! ' + image + 'displayed.');

    Теперь вместо третьего сообщения должна возникнуть следующая ошибка:

    -
    TypeError: image is undefined; can't access its "src" property
    +
    TypeError: image is undefined; can't access its "src" property

    Это происходит потому, что в то же время браузер пытается запустить третий console.log(), блок fetch() ещё не закончил выполнение, поэтому переменная image ещё не имеет значения.

    diff --git a/files/ru/learn/javascript/asynchronous/timeouts_and_intervals/index.html b/files/ru/learn/javascript/asynchronous/timeouts_and_intervals/index.html index 8d00ba98af..19019a19a9 100644 --- a/files/ru/learn/javascript/asynchronous/timeouts_and_intervals/index.html +++ b/files/ru/learn/javascript/asynchronous/timeouts_and_intervals/index.html @@ -64,13 +64,13 @@ original_slug: Learn/JavaScript/Asynchronous/Таймауты_и_интерва

    В следующем примере, браузер будет ожидать две секунды перед тем как  выполнит анонимную функцию, тогда отобразит сообщение (живой пример, и исходный код):

    -
    let myGreeting = setTimeout(function() {
    +
    let myGreeting = setTimeout(function() {
       alert('Hello, Mr. Universe!');
     }, 2000)

    Указанные вами функции не обязательно должны быть анонимными. Вы можете дать своей функции имя и даже определить её где-нибудь ещё и передать ссылку на функцию в setTimeout (). Следующие две версии фрагмента кода эквивалентны первой:

    -
    // С именованной функцией
    +
    // С именованной функцией
     let myGreeting = setTimeout(function sayHi() {
       alert('Hello, Mr. Universe!');
     }, 2000)
    @@ -92,19 +92,19 @@ let myGreeting = setTimeout(sayHi, 2000);

    Например, вы можете реорганизовать предыдущую функцию, чтобы она передавала привет любому имени, переданному ей:

    -
    function sayHi(who) {
    +
    function sayHi(who) {
       alert(`Hello ${who}!`);
     }

    Теперь вы можете передать имя в вызов setTimeout () в качестве третьего параметра:

    -
    let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');
    +
    let myGreeting = setTimeout(sayHi, 2000, 'Mr. Universe');

    Очистка таймаутов

    Наконец, если был создан тайм-аут, вы можете отменить его до истечения указанного времени, вызвав clearTimeout(), передав ему идентификатор вызова setTimeout() в качестве параметра. Итак, чтобы отменить указанный выше тайм-аут, вы должны сделать следующее:

    -
    clearTimeout(myGreeting);
    +
    clearTimeout(myGreeting);

    Note: См.greeter-app.html для более полной демонстрации, которая позволяет вам указать имя для приветствия и отменить приветствие с помощью отдельной кнопки (см. исходный код).

    @@ -118,7 +118,7 @@ let myGreeting = setTimeout(sayHi, 2000);

    Давайте посмотрим на пример. Следующая функция создаёт новый объект Date(), с помощью toLocaleTimeString() извлекает из него строку с временем и отображает её в пользовательском интерфейсе. Затем он запускает функцию один раз в секунду с помощью setInterval(), создавая эффект цифровых часов, которые обновляются раз в секунду ( реальный пример, и исходный код):

    -
    function displayTime() {
    +
    function displayTime() {
        let date = new Date();
        let time = date.toLocaleTimeString();
        document.getElementById('demo').textContent = time;
    @@ -132,7 +132,7 @@ const createClock = setInterval(displayTime, 1000);

    setInterval () выполняет задачу постоянно. setInterval () продолжает выполнять задачу вечно, если вы что-то с ней не сделаете. Возможно, вам понадобится способ остановить такие задачи, иначе вы можете получить ошибки, если браузер не сможет выполнить какие-либо другие версии задачи или если анимация, обрабатываемая задачей, завершилась. Вы можете сделать это так же, как останавливаете timeouts - передавая идентификатор, возвращаемый вызовом setInterval (), в функцию clearInterval ():

    -
    const myInterval = setInterval(myFunction, 2000);
    +
    const myInterval = setInterval(myFunction, 2000);
     
     clearInterval(myInterval);
    @@ -182,7 +182,7 @@ clearInterval(myInterval);

    В приведённом ниже примере используется рекурсивный setTimeout () для запуска переданной функции каждые 100 миллисекунд:

    -
    let i = 1;
    +
    let i = 1;
     
     setTimeout(function run() {
       console.log(i);
    @@ -192,7 +192,7 @@ setTimeout(function run() {
     
     

    Сравните приведённый выше пример со следующим - здесь используется setInterval () для достижения того же эффекта:

    -
    let i = 1;
    +
    let i = 1;
     
     setInterval(function run() {
       console.log(i);
    @@ -217,7 +217,7 @@ setInterval(function run() {
     
     

    Например, код приведённый ниже (рабочий код) выводит alert содержащий "Hello", затем alert содержащий "World" как только вы нажмёте ОК в первом alert.

    -
    setTimeout(function() {
    +
    setTimeout(function() {
       alert('World');
     }, 0);
     
    @@ -243,7 +243,7 @@ alert('Hello');

    Метод принимает в качестве аргумента колбэк, который должен быть вызван перед перерисовкой. Это общий шаблон, в котором он используется:

    -
    function draw() {
    +
    function draw() {
        // Drawing code goes here
        requestAnimationFrame(draw);
     }
    @@ -272,7 +272,7 @@ draw();

    Давайте поговорим ещё немного о том, чем метод requestAnimationFrame () отличается от других методов, используемых ранее. Глядя на наш код сверху:

    -
    function draw() {
    +
    function draw() {
        // Drawing code goes here
        requestAnimationFrame(draw);
     }
    @@ -281,7 +281,7 @@ draw();

    Такой же код с использованием setInterval():

    -
    function draw() {
    +
    function draw() {
        // Drawing code goes here
     }
     
    @@ -297,7 +297,7 @@ setInterval(draw, 17);

    Это полезно, поскольку позволяет запускать вещи в определённое время и в постоянном темпе, независимо от того, насколько быстрым или медленным может быть ваше устройство. Общий шаблон, который вы бы использовали, выглядит примерно так:

    -
    let startTime = null;
    +
    let startTime = null;
     
     function draw(timestamp) {
         if (!startTime) {
    @@ -337,7 +337,7 @@ draw();
  • Примените следующий CSS к HTML шаблону (любым предпочитаемым способом). Он установ красный фон на странице, высоту <body> равную 100% высоты {{htmlelement("html")}} , и центрирует <div> внутри <body>, по горизонтали и вертикали.

    -
    html {
    +  
    html {
       background-color: white;
       height: 100%;
     }
    @@ -362,7 +362,7 @@ div {
      
  • Разместите следующий JavaScript-код в  <script> . Здесь вы сохраняете ссылку на <div> внутри, устанавливаете для переменной rotateCount значение 0, устанавливаете неинициализированную переменную, которая позже будет использоваться для хранения ссылки на вызов requestAnimationFrame(), и устанавливаете для переменной startTime значение null, которая будет позже использоваться для хранения времени начала requestAnimationFrame().

    -
    const spinner = document.querySelector('div');
    +  
    const spinner = document.querySelector('div');
     let rotateCount = 0;
     let startTime = null;
     let rAF;
    @@ -371,14 +371,14 @@ let rAF;
      
  • Под предыдущим кодом вставьте функцию draw() которая будет использоваться для хранения нашего кода анимации, который включает параметр timestamp :

    -
    function draw(timestamp) {
    +  
    function draw(timestamp) {
     
     }
  • Внутри draw () добавьте следующие строки. Они определят время начала, если оно ещё не определено (это произойдёт только на первой итерации цикла), и установят для параметра rotateCount значение для поворота счётчика (текущая временная метка, возьмите начальную временную метку, разделённую на три, чтобы замедлиться):

    -
      if (!startTime) {
    +  
      if (!startTime) {
        startTime = timestamp;
       }
     
    @@ -388,22 +388,22 @@ let rAF;
      
  • Под предыдущей строкой внутри draw () добавьте следующий блок - он проверяет, превышает ли значение rotateCount 359 (например, 360, полный круг). Если это так, он устанавливает значение по модулю 360 (то есть остаток, оставшийся после деления значения на 360), поэтому круговая анимация может продолжаться непрерывно с разумным низким значением. Обратите внимание, что это не является строго необходимым, но легче работать со значениями от 0 до 359 градусов, чем со значениями типа «128000 градусов».

    -
    if (rotateCount > 359) {
    +  
    if (rotateCount > 359) {
       rotateCount %= 360;
     }
  • Затем, под предыдущим блоком, добавьте следующую строку, чтобы вращать spinner: -
    spinner.style.transform = `rotate(${rotateCount}deg)`;
    +
    spinner.style.transform = `rotate(${rotateCount}deg)`;
  • В самом низу внутри функции draw () вставьте следующую строку. Это ключ ко всей операции - вы устанавливаете для переменной, определённой ранее, активный вызов requestAnimation (), который принимает функцию draw () в качестве своего параметра. Это запускает анимацию, постоянно выполняя функцию draw () со скоростью, близкой к 60 FPS.

    -
    rAF = requestAnimationFrame(draw);
    +
    rAF = requestAnimationFrame(draw);
  • Ниже, вызовите функцию draw() для запуска анимации.

    -
    draw();
    +
    draw();
  • @@ -417,7 +417,7 @@ let rAF;

    Просто передайте ему значение, возвращаемое вызовом requestAnimationFrame () для отмены, которое вы сохранили в переменной rAF:

    -
    cancelAnimationFrame(rAF);
    +
    cancelAnimationFrame(rAF);

    Активное обучение: запуск и остановка нашей анимации

    @@ -444,7 +444,7 @@ let rAF;

    В этом примере вы должны анимировать как положение персонажа на экране, так и отображаемый спрайт. В анимации спрайта всего 6 кадров. Если бы вы показывали разные кадры спрайта для каждого кадра, отображаемого на экране, с помощью requestAnimationFrame (), Guybrush двигал бы конечностями слишком быстро, и анимация выглядела бы нелепо. Следовательно, в этом примере регулируется скорость, с которой спрайт циклически повторяет свои кадры, используя следующий код:

    -
    if (posX % 13 === 0) {
    +
    if (posX % 13 === 0) {
       if (sprite === 5) {
         sprite = 0;
       } else {
    @@ -456,7 +456,7 @@ let rAF;
     
     

    ... Фактически, это примерно каждые 6,5 кадров, поскольку мы обновляем posX (положение персонажа на экране) на два кадра:

    -
    if (posX > width/2) {
    +
    if (posX > width/2) {
       newStartPos = -( (width/2) + 102 );
       posX = Math.ceil(newStartPos / 13) * 13;
       console.log(posX);
    @@ -485,7 +485,7 @@ let rAF;
      
  • Внутри пустого элемента {{htmlelement("script")}} на вашей странице, начните с добавления следующих строк кода, которые определяют некоторые переменные и константы, которые вам понадобятся в дальнейшем:

    -
    const spinner = document.querySelector('.spinner p');
    +  
    const spinner = document.querySelector('.spinner p');
     const spinnerContainer = document.querySelector('.spinner');
     let rotateCount = 0;
     let startTime = null;
    @@ -508,7 +508,7 @@ const result = document.querySelector('.result');
  • Ниже добавьте следующую функцию. Она просто берёт два числа и возвращает случайное число между ними. Это понадобится вам позже, чтобы сгенерировать случайный интервал ожидания.

    -
    function random(min,max) {
    +  
    function random(min,max) {
       var num = Math.floor(Math.random()*(max-min)) + min;
       return num;
     }
    @@ -516,7 +516,7 @@ const result = document.querySelector('.result');
  • Затем добавьте функцию draw(), которая анимирует спиннер. Это очень похоже на версию из предыдущего примера простого счётчика:

    -
    function draw(timestamp) {
    +  
    function draw(timestamp) {
       if(!startTime) {
        startTime = timestamp;
       }
    @@ -534,13 +534,13 @@ const result = document.querySelector('.result');
  • Теперь пришло время настроить начальное состояние приложения при первой загрузке страницы. Добавьте следующие две строки, которые просто скрывают абзац результатов и контейнер счётчика с помощью display: none ;.

    -
    result.style.display = 'none';
    +  
    result.style.display = 'none';
     spinnerContainer.style.display = 'none';
  • Затем определите функцию reset (), которая возвращает приложение в исходное состояние, необходимое для повторного запуска игры после её завершения. Добавьте в конец кода следующее:

    -
    function reset() {
    +  
    function reset() {
       btn.style.display = 'block';
       result.textContent = '';
       result.style.display = 'none';
    @@ -549,7 +549,7 @@ spinnerContainer.style.display = 'none';
  • Хорошо, хватит подготовки! Пришло время сделать игру доступной! Добавьте в свой код следующий блок. Функция start () вызывает draw (), чтобы запустить вращение спиннера и отобразить его в пользовательском интерфейсе, скрыть кнопку Start, чтобы вы не могли испортить игру, запустив её несколько раз одновременно, и запускает вызов setTimeout (), который выполняется функция setEndgame () по прошествии случайного интервала от 5 до 10 секунд. Следующий блок также добавляет обработчик событий к вашей кнопке для запуска функции start () при её нажатии.

    -
    btn.addEventListener('click', start);
    +  
    btn.addEventListener('click', start);
     
     function start() {
       draw();
    @@ -569,7 +569,7 @@ function start() {
      
  • Добавьте в свой код следующую функцию:

    -
    function setEndgame() {
    +  
    function setEndgame() {
       cancelAnimationFrame(rAF);
       spinnerContainer.style.display = 'none';
       result.style.display = 'block';
    -- 
    cgit v1.2.3-54-g00ecf