выполнить .innerHTML в функции - PullRequest
       15

выполнить .innerHTML в функции

0 голосов
/ 11 апреля 2019

Я хочу изменить свой HTML до завершения кода js.

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

function wait(ms){
  var start = new Date().getTime();
  var end = start;
  while(end < start + ms) {
    end = new Date().getTime();
  }
}

function one() {
  wait(1000);
  document.getElementById("1a").innerHTML = "bla";
}

function two() {
  wait(1000);
  document.getElementById("2b").innerHTML = "blabla";
}

function three() {
  wait(1000);
  document.getElementById("3c").innerHTML = "blablabla";
}

function start() {
  one();
  two();
  three();
} start();

код ждет 3 секунды, а затем обновляет мои div.

Я хочу, чтобы мой код:

подождите 1 секунду, обновить div, подождите 1 секунду, обновить div, подождите 1 секунду, обновить div

Ответы [ 3 ]

1 голос
/ 11 апреля 2019

Это разрешимо с Promise s:

function showTextAfterMS (text, elemSelector, ms) {
  return new Promise((res, rej) => {
    let elem = document.querySelector(elemSelector);
    if (!elem) {
      return rej(new Error(`Cannot find element by selector: ${elemSelector}`));
    }
    
    setTimeout(() => {
      elem.innerHTML = text;
      res(elem);
    }, ms);
  });
}



showTextAfterMS('bla', '#el1', 1000).
  then(() => showTextAfterMS('blabla', '#el2', 1000)).
  then(() => showTextAfterMS('blablabla', '#el3', 1000));
<div id="el1"></div>
<div id="el2"></div>
<div id="el3"></div>

Вы также можете сделать это с setTimeout или setInterval, но мой опыт показывает, что использование Promise немного более надежно / стабильно.

Чтобы отобразить первый текст без задержки, просто измените первый вызов на showTextAfterMS на showTextAfterMS('bla', '#el1', 0).

Редактировать

Причина, по которой использование Promise s является правильным решением, в конечном счете коренится в концепциях времени выполнения JavaScript. Вкратце, это потому, что технически setTimeout, а также setInterval являются асинхронными действиями , потому что они оба обрабатываются циклом событий JavaScript . Подробное объяснение цикла событий и общей модели параллелизма JavaScript можно найти в MDN .

Вкратце: каждое действие, которое должно быть выполнено, переносится в конец Queue действий, которые должна выполнить среда выполнения, ... ну, в общем, запустить. Таким образом, обрабатываются действия из пользовательского интерфейса, а также другие действия, такие как тайм-ауты и интервалы. Среда выполнения обрабатывает эти действия шаг за шагом, однако им может потребоваться другое время для завершения . Это связано с тем, что среда выполнения запускается до завершения, что означает, что каждое действие обрабатывается после драгоценное действие было обработано полностью. Поскольку действия, производимые setTimeout и setInterval, помещаются в Queue, количество миллисекунд не является гарантированным временем, когда соответствующие функции называются . Это гарантированный минимальный промежуток времени , который истекает до их выполнения. Это заставляет их обоих производить асинхронных действий .

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

Немного помахав рукой, мы можем сказать, что мы можем прийти к тому же решению без Promise, используя функции обратного вызова, из-за того, как работает цикл обработки событий. Он уже вызывает одну функцию за раз , верно? Итак, вот «равное» решение, основанное на обратных вызовах:

// A "Promise equivalent", setTimeout based function with callbacks
function showTextAfter (ms, text, elemSelector, onComplete, onError) {
  if (typeof ms !== 'number' || isNaN(ms)) {
    return onError(new Error(`MS needs to be number, got: ${ms}`));
  }
  if (typeof text !== 'string') {
    return onError(new Error(`Expected TEXT to be String, got: ${text}`));
  }
  let elem = document.querySelector(elemSelector);
  if (!elem) {
    return onError(new Error(`Cannot find element: ${elemSelector}`));
  }

  setTimeout(() => {
    elem.innerHTML = text;
    onComplete(elem);
  }, ms);
}



showTextAfter(1000, 'bla', '#el1', (elem1) => {
  showTextAfter(1000, 'blabla', '#el2', (elem2) => {
    showTextAfter(1000, 'blablabla', '#el3', (elem3) => {
      // do whatever you want with the elements. this example
      // discards them
    });
  });
});
<div id="el1"></div>
<div id="el2"></div>
<div id="el3"></div>

Он работает одинаково хорошо и позволяет вам надежно связывать свои действия. Недостатки этого:

  • Не очень хорошо масштабируется. Чтобы связать больше действий, вам нужно более глубокое вложение, потому что новые действия должны вызываться внутри функции обратного вызова success .
  • Вы должны сделать это вложение вручную . Поэтому чем глубже вы будете вкладывать, тем сложнее будет отслеживать поток кода. Вот почему люди называют это «пирамида гибели». Представьте, что вам нужно смешать текст для 20 элементов.
  • Посмотрите на его подпись: showTextAfter :: Number -> String -> String -> Function -> Function -> undefined. Это много вещей для этой жалкой маленькой функции! Не было бы здорово просто передать первые 3 аргумента?

Мы можем несколько смягчить последнюю проблему, вернув новую функцию из вызова на showTextAfter, которая использует обратные вызовы onComplete и onError:

function showTextAfter (ms, text, elemSelector) {
  return function (onComplete, onError) { // <-- this little fellow here is what it's all about
    if (typeof ms !== 'number' || isNaN(ms)) {
      return onError(new Error(`MS needs to be number, got: ${ms}`));
    }
    if (typeof text !== 'string') {
      return onError(new Error(`Expected TEXT to be String, got: ${text}`));
    }
    let elem = document.querySelector(elemSelector);
    if (!elem) {
      return onError(new Error(`Cannot find element: ${elemSelector}`));
    }

    setTimeout(() => {
      elem.innerHTML = text;
      onComplete(elem);
    }, ms);
  }
}


const showEl1 = showTextAfter(1000, 'bla', '#el1');
const showEl2 = showTextAfter(1000, 'blabla', '#el2');
const showEl3 = showTextAfter(1000, 'blablabla', '#el3');

showEl1(elem1 => {
  showEl2(elem2 => {
    showEl3(elem3 => {
      // whatever
    });
  });
});
<div id="el1"></div>
<div id="el2"></div>
<div id="el3"></div>

Да, так лучше. Но это не действительно не решает проблему, верно?

Не паникуйте, потому что это именно те проблемы, которые решает Promise s! Они позволяют упорядочивать асинхронные действия в масштабируемом режиме с гораздо более простым отслеживанием потока управления . Все вышеперечисленные проблемы могут быть устранены с помощью (нативного) возвращаемого значения «на месте», к которому можно «связать» другие асинхронные шаги, которые будут выполнены «в будущем» (то есть они могут быть выполнены успешно или могут быть выполнены с помощью Error). По-настоящему разумно то, что Promise позволяет вам связать следующее асинхронное действие без вложенности .

См. Мой первоначальный (немного измененный) ответ:

function showTextAfterMS (ms, text, elemSelector) {
  return new Promise((onComplete, onError) => {

    // type checking stuff...

    let elem = document.querySelector(elemSelector);
    setTimeout(() => {
      elem.innerHTML = text;
      onComplete(elem);
    }, ms);
  });
}



showTextAfterMS(1000, 'bla', '#el1'). // <-- no more nesting!
  then(() => showTextAfterMS(1000, 'blabla', '#el2')).
  then(() => showTextAfterMS(1000, 'blablabla', '#el3'));
<div id="el1"></div>
<div id="el2"></div>
<div id="el3"></div>
1 голос
/ 11 апреля 2019

Вы можете попробовать использовать setTimeout .

Использование функции

Я сделал эту функцию, которая упрощает предоставление массива объектов, которые вы хотите обновить.

/* 
  timed_elemet_updates(array, int)
  `elements` should be an array of objects which contains the
  ID and HTML you want to update.
*/
function timed_element_updates(elements, seconds) {
      var element = elements.shift();
      document.getElementById(element.id).innerHTML = element.html;
      setTimeout(timed_element_updates, (seconds * 1000), elements, seconds);
}

Пример использования:

function start() {
    var elements = [
        {id: '1a', html: 'bla'},
        {id: '2b', html: 'blabla'},
        {id: '3c', html: 'blablabla'},
    ];
    timed_element_updates(elements, 1);
}
start();

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

Оригинальный ответ

function one() {
    document.getElementById("1a").innerHTML = "bla";
    setTimeout(two, 1000);
}

function two() {
    document.getElementById("2b").innerHTML = "blabla";
    setTimeout(three, 1000);
}

function three() {
  document.getElementById("3c").innerHTML = "blablabla";
}

one();

Это вызовет функцию one(), затем она вызовет two() через 1000 миллисекунд (1 секунда), затем она вызовет three() через 1000 миллисекунд (1 секунда).

0 голосов
/ 11 апреля 2019

Вы можете воспользоваться async functions:

function one() {
  console.log('document.getElementById("1a").innerHTML = "bla";')
}

function two() {
  console.log('document.getElementById("2b").innerHTML = "blabla";')
}

function three() {
  console.log('document.getElementById("3c").innerHTML = "blablabla";')
}

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

async function demo() {
  await sleep(1000);
  one();
  await sleep(1000);
  two();
  await sleep(1000);
  three();
}

demo();

Ссылка: https://javascript.info/async-await

...