Задать вопрос
Ответы пользователя по тегу JavaScript
  • Почему именно такой порядок вывода в консоль?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Исследования в первоначальной версии ответа (ниже) натолкнули на поведение, которое смог объяснить Jonas Wilms в ответе на SO.

    Возврат промиса из C1 then() создаёт ТРИ последовательных микротаска:
    1. NewPromiseResolveThenableJob, который вызывает then() на том уже отресолвленном промисе, что вернули в C1. И т.к. этот промис уже отресолвлен, сразу же в очередь вставляется следующий микротаск:
    2. NewPromiseReactionJob этого уже разрешенного промиса, и этот, в свою очередь, добавляет третью микрозадачу:
    3. ещё одну NewPromiseReactionJob, которая уже выводит в лог "2".


    Если возвращать не промис (и не thenable объект), а "простое" значение или undefined, то добавляется всего один микротаск.

    Раньше подобная задержка в три шага была и для await, но потом её оптимизировали в движке V8. А вот про then() забыли.

    Подробнее стоит посмотреть ответ на SO, ссылка в начале.

    [первая версия ответа]

    Интересный вопрос! Больше экспериментов.
    Вспомогательная функция для читаемости:
    const log = (value, returnPromise) => () => {
      console.log(value);
      if (returnPromise) return Promise.resolve();
    };
    Возвращает функцию, которая обычный console.log() с переданным значением. И если второй аргумент трушный, то вернёт отрезолвленный промис.

    Эксперимент 1. «Застёжка-молния»
    Promise.resolve()
      .then(log('a1'))
      .then(log('a2'))
      .then(log('a3'))
      .then(log('a4'))
      .then(log('a5'))
      .then(log('a6'))
    ;
    
    Promise.resolve()
      .then(log('b1'))
      .then(log('b2'))
      .then(log('b3'))
      .then(log('b4'))
      .then(log('b5'))
      .then(log('b6'))
    ;
    Выводит поочередные a1 b1 a2 b2 a3 b3 ...
    Эксперимент 2. «Отстаём на 2»

    Единственное отличие: в первом А возвращаем отресолвленный промис.
    Promise.resolve()
      .then(log('a1', true))
      .then(log('a2'))
      .then(log('a3'))
      .then(log('a4'))
      .then(log('a5'))
      .then(log('a6'))
    ;
    
    Promise.resolve()
      .then(log('b1'))
      .then(log('b2'))
      .then(log('b3'))
      .then(log('b4'))
      .then(log('b5'))
      .then(log('b6'))
    ;

    Выводит
    a1 b1 b2 b3 a2 b4 a3 b5 a4 b6 a5 a6
    А-шки после 1-й отстают на 2, пропустив вперёд b2 и b3.

    Очередь микрозадач работает как FIFO буфер: первый пришёл, первый ушёл.

    Цепочка из then() выполняется асинхронно. После выполнения очередного, создаётся следующий microtask. Несколько цепочек, как видно из 1-го эксперимента, выполняются параллельно-пошагово, «молнией».

    Возврат созданного выполненного промиса, и ожидание его разрешения вызывает задержку в очереди микрозадач на 1 микротаск + «перемещение» цепочки "А" в конец (меняется порядок a-b => b-a):
    "молния"
    a1 b1 ; a2 b2 ; a3 b3 ; a4 b4 ; a5 b5 ; a6 b6
    
    с промисом в А1
    a1 b1 ; b2 ; b3 a2 ; b4 a3 ; b5 a4 ; b6 a5 ; a6


    Подробнее с тремя цепочками

    На этот раз три цепочки промисов "a", "b", "c". Цепочка "А" вернёт промис на 1-м шаге, "С" — на 4-м. Код эксперимента:
    const output = [];
    
    const makeChain = (key, n = 5, trueStep = false) => {
      let p = Promise.resolve();
      const arrow = isArrow => isArrow ? '->' : '';
      for (let i = 1; i <= n; i++) {
        const returnPromise = trueStep === i;
        const afterPromise = trueStep === i - 1;
        p = p.then(() => {
          output.push(`${arrow(afterPromise)}${key}${i}${arrow(returnPromise)}`);
          if (returnPromise) return Promise.resolve();      
        });
      }
      return p.catch(console.error);
    };
    
    const n = 7;
    makeChain('a', n, 1),
    makeChain('b', n),
    makeChain('c', n, 4),
    
    // мАкрозадача выполнится после всех мИкрозадач:
    setTimeout(() => console.log(output.join(' ')));

    Результат c вручную добавленными разбивками:
    a1-> b1 c1 ; b2 c2 ; b3 c3 ->a2 ; b4 c4-> a3 ; b5 a4 ; b6 a5 ; b7 ->c5 a6 c6 ; a7 c7
    Тут всё ещё не вполне мне понятен порядок после возврата промиса из C4.
    Ответ написан
  • Как отключить предупреждение при использовании document.write()?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Очень не рекомендуется более использовать метод document.write() (см. ссылку именно на англ. т.к. перевод запаздывает)

    Можно убрать зачеркивание устаревшего метода write() в вашем коде:
    в настройках найдите параметр editor.showDeprecated и установите его в false (уберите галочку).

    Это не изменит того, что writeln() будет приоритетнее, как актуальный метод, в отличие от устаревшего write()
    Ответ написан
    Комментировать
  • Как добавить required к форме с выпадающим js календарём?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Судя по коду (а ведь могли бы сами написать, что используется FlatPickr и jQuery Form), стоит дополнить именно отправку формы: добавить валидацию данных до отправки, и в случае null в поле даты (по умолчанию там null, в "0000-00-00" преобразовывается где-то после), прекращать дальнейшую отправку формы, а юзеру показывать напоминалку ввести дату рождения.

    См. beforeSerialize или beforeSubmit в опциях jQuery Form. Там проверяйте, что в поле даты рождения, и если null (или минимальная допустимая дата) – даже и не отправляйте форму.
    Ответ написан
    Комментировать
  • Почему переменная теряет видимость?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Похоже на баг/фичу консоли Google Chrome.
    В консоли FireFox повторные запуски корректно ругаются на переобъявление let testValue

    Так действительно делать нельзя, но разрабы Chrome с версии 80 решили «помочь» разработчикам, использующим консоль, и ленящимся заключать весь код в фигурные скобки, чтобы изолировать область видимости. С FireFox такая привычка у меня лично уже выработалась, все эксперименты в консоли – внутри { }

    И вот в консоли Хрома теперь можно переназначать и переенные let, и даже const. И классы. Печаль.

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

    TL&DR; предлагаю писать корректный JS в строгих песочницах, где два let подряд не пройдут.
    Ответ написан
    2 комментария
  • Как вывести число столько раз, сколько написано в input?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Вот так можно повторить строку N раз, используя метод строки repeat():
    ' + 8'.repeat(3)
    // получится строка " + 8 + 8 + 8"
    Ответ написан
    2 комментария
  • Как ограничить доступ определенному пользователю в своем телеграмм боте?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Бот получает Update.
    Скорее всего интересуют апдейты с сообщением – объектом Message в поле message.
    Объект Message содержит необязательное поле from с объектом User.
    У объекта User есть поле id.

    При обработке update надо первым делом вытаскивать этот id, и если он равен id заблокированного пользователя, далее ничего не обрабатывать.

    const badUserId = 12345; // id плохого парня
    const update = ... ; // TODO: как-то получили в боте очередной update
    
    if (update?.message?.from?.id === badUserId) {
      return;
    }
    Ответ написан
    2 комментария
  • Почему появляется ошибка Uncaught Reference?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Область переменной i заканчивается точкой-с-запятой после for() и снаружи — что до, что после — её нет.

    Вот так другое дело:
    let arr = ["John", "Mike", "Bill"];  
    for (let i = 0; i < arr.length; i++)
      console.log(arr[i]);

    Или, более общепринято, с фигурными скобками:
    const arr = ["John", "Mike", "Bill"]; // const, т.к. arr не меняется, лучше
    for (let i = 0; i < arr.length; i++) {
      // тут, внутри этих скобок, i определена
      console.log(arr[i]);
    }


    Почитайте про переменные и способы их объявления. Скажем, с устаревшим var i все бы работало и в вашем варианте.
    Ответ написан
  • Как на чистом JS отобрать HTML элементы по определенным признакам?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Примерно так можно:
    - при клике передавать имя класса, и всего один общий обработчик
    - перечислить все классы, чтобы выбранный включать, а остальные выключать
    Ответ написан
    Комментировать
  • Что происходит со свойством класса при присвоении извне?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Вы почувствовали разницу между «статическими» свойствами/методами и просто свойствами/методами.

    Статические – принадлежат самому классу, но не его экземплярам.
    Ответ написан
    Комментировать
  • Какая логика у перебора массива таким способом?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    i — это индекс массива — номер позиции в нём, считая от 0.
    arr[i]значение, которое лежит в массиве arr в позиции i.
    ещё проще
    Массив представьте как ряд одинаковых коробочек, юпронумерованных от 0 до N.
    В коробочках что-то лежит. Обратиться к коробку можно по имени массива и номеру ячейки: arr[4] например – это пятая по счету коробка, т.к. счет идёт от 0.


    Последовательность получается примерно такая:
    min = 12;

    i = 0; arr[i] = 12; arr[i] меньше min? НЕТ. идём дальше
    i = i + 1
    i = 1; arr[i] = 5; arr[i] меньше min? ДА. min становится 5; идём дальше
    i = i + 1
    i = 2; arr[i] = 8; arr[i] меньше min? НЕТ. идём дальше
    i = i + 1
    ...
    i = 4; arr[i] = 3; arr[i] меньше min? ДА. min становится 3; идём дальше
    ...
    for закончил работу, min так и остался равным 3
    Ответ написан
    Комментировать
  • Объясните, почему так работает код?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    потому, что воспринимает как десятичную точку
    2000.123

    Вот так, например, сработает: 2000.0.toLocaleString() — тут уже понятно, что не десятичная точка. Но я — за скобки!
    Ответ написан
    2 комментария
  • Как в js обновить список нод в querySelectorAll и их EventListener после удаления ноды?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    слушайте событие клика как jQuery – не на каждой кнопке по отдельности, а один раз на общем родителе: на этом .show-cart

    В обработчике проверяйте, по чему именн, там, где-то в дочерних, кликнули (см. event.target). Если у кликнутого есть класс .delete-item, то действуем далее.

    Слушать на общем родителе – называется волшебным термином «делегирование событий».
    Ответ написан
    1 комментарий
  • Как подставить значение переменной в javascript?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Попробуйте так
    const centerString = '56.964949, 60.623745';
    
    // строку разбиваем в массив чисел
    const center = centerString.split(', ').map(Number);
    const zoom = 12;
    
    const params = {
      center,
      zoom,
    };
    
    const init = () => {
      const myMap = new ymaps.Map("map", params);
    }
    
    ymaps.ready(init);
    Ответ написан
    Комментировать
  • Как запустить цикл for of для длинны массива?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Обычный цикл чем не цикл?
    const chain = async times => {
      for (let n = times; n > 0; n--) {
        console.log(`${n} starting..`);
        await new Promise(resolve => setTimeout(resolve, 800));
        console.log(`${n} ..finished`);
      }
    };
    
    chain(4).then(() => console.log('Done'));
    Ответ написан
    Комментировать
  • Как заблокировать работу с окном браузера при перезагрузке страницы?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Можно начать перехватывать вообще все события клика по html>body на начальной стадии "capture", и дальше не пропускать. Что-то типа
    document.body.addEventListener('click', e => {
        e.stopPropagaion();
        e.stopImmediatePropagation();
        e.preventDefault();
      }, { capture: true });
    Аналогично можно и для клавиатурных событий попробовать.
    Ответ написан
    Комментировать
  • Как вывести оставшееся время до определенного часа?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Можно прибавлять 24 часа там, где что-то меньше чего-то..

    Примерно так, наскоро. Подробно не проверял:
    function workHours(on, off, testNow = null) {
      // TODO: validate input
      const now = testNow ?? new Date().getHours();
      const a = Number(on);
      let b = Number(off);
    
      const format = (status, nextTime) => ({ status, next_time: nextTime });
    
      if (a === b) {
        return format(true, 0);
      }
    
      if (a > b) {
        b += 24;
      }
    
      if (now >= a && now < b) {
        return format(true, 0);
      }
    
      if (now < a) {
        return format(false, a - now);
      }
    
      return format(false, a + 24 - now);
    }

    Тесты

    // Тесты тесты
    const tests = [
      [9, 17, 10, { status: true, next_time: 0 }],
      [9, 17, 8, { status: false, next_time: 1 }],
      [9, 17, 17, { status: false, next_time: 16 }],
      [19, 17, 17, { status: false, next_time: 2 }],
      [19, 19, 17, { status: true, next_time: 0 }],
    ];
    
    const eq = (a, b) => Object.entries(a).every(([k, v]) => b[k] === v);
    
    tests.forEach(test => {
      const [on, off, now, expected] = test;
      const result = workHours(on, off, now);
      const testResult = eq(result, expected);
      if (testResult) {
        console.log('Passed', {on, off, now, result});
      }
      console.assert(testResult, test);
    });
    Ответ написан
    Комментировать
  • Как объединить строки в файле gedcom?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Готовый модуль: https://www.npmjs.com/package/gedcom
    Ответ написан
    Комментировать
  • Как получить таблицу Google sheets как массив?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    вроде бы так должно получиться:

    - console.log(data.data.values)
    + console.log(data.values)

    Метод spreadsheets.values/get возвращает объект ValueRange со свойствами range, majorDimension и values. Последнее и интересует.
    Ответ написан
    Комментировать
  • Можно ли присвоить массив ключам объекта?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Завезли нечитаемые макароны!
    (arr => Object.fromEntries(['a', 'b', 'c'].map((p, i) => [p, arr[i]])))([1, 2, 3])
    // Object { a: 1, b: 2, c: 3 }
    
    // или так, что чуть короче, но такой же фарс
    (arr => Object.fromEntries(Array.from('abc', (p, i) => [p, arr[i]])))([1, 2, 3])
    Ответ написан
    1 комментарий
  • Как в массиве обозначить нескольким параметрам одно значение?

    sergiks
    @sergiks Куратор тега JavaScript
    ♬♬
    Как один из вариантов, пишите свойства как диапазоны.

    Подзадача: из массива, где могут быть и строки "20-80" найти строку, удовлетворяющую искомому значению
    const findRangeKey = (keysArray, keyString) => {
      const num = Number(keyString);
      if (keysArray.includes(keyString)) {
        // exact match
        return keyString;
      }
    
      return keysArray.find(key => {
        const [min, max] = key.split('-').map(Number);
        if (max !== undefined) {
          return num >= min && num <= max;
        }
    
        return num === min;
      });
    };
    
    findRangeKey(['10', '20-40'], 30); // "20-40"
    findRangeKey(['10', '20-40'], 20); // "20-40"
    findRangeKey(['10', '20-40'], 10); // "10"


    И теперь можно искать в словаре с диапазонами:
    const data = {
      '10': {
        '5-15': {
          title: '10-5-15',
        }
      },
      '20-60': {
        '5': { title: '20-60-5' },
        '10': { title: '20-60-10' },
        '20-40': { title: '20-60-20-40' },
      },
    };
    
    const getTitle = (data, a, b) => {
      const aKey = findRangeKey(Object.keys(data), a);
      if (!aKey) {
        return null;
      }
    
      const bKey = findRangeKey(Object.keys(data[aKey]), b);
      if (!bKey) {
        return null;
      }
    
      return data[aKey][bKey].title;
    };
    
    console.log(getTitle(data, '10', '5')); // "10-5-15"
    console.log(getTitle(data, 40, 30)); // "20-60-20-40"
    Ответ написан