在函数中执行 .innerHTML

execute .innerHTML in a function

我想在js代码结束前更改我的HTML

我尝试使用异步函数,但我不确定这是否是正确的方法。

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 秒,然后更新我的 divs。

我希望我的代码:

等1秒, 更新 div, 等待 1 秒, 更新 div, 等待 1 秒, 更新 div

您可以尝试使用 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(),然后它会在 1000 毫秒(1 秒)后调用 two(),然后它会在 1000 毫秒(1 秒)后调用 three()

你可以利用 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

这可以用 Promises 解决:

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>

您也可以使用 setTimeoutsetInterval,但我的经验是使用 Promise 会更 reliable/stable。

要立即显示第一个文本,只需将对 showTextAfterMS 的第一次调用更改为 showTextAfterMS('bla', '#el1', 0)

编辑

之所以使用 Promises 是正确的解决方案,最终植根于 JavaScript 的 运行 时间概念。简而言之,这是因为技术上 setTimeoutsetInterval 都是 异步操作 ,因为它们都由 JavaScript 事件循环。可以在 MDN.

上找到 JavaScript 的事件循环和一般并发模型的详尽解释

简而言之:必须执行的每个操作都被推到 运行 时间必须执行的 Queue 个操作的末尾,...好吧,运行。这样,来自 UI 的操作以及超时和间隔等其他操作都会得到处理。 运行时间会逐步处理这些操作,但是,它们可能需要不同的时间才能完成。那是因为 运行 时间 运行 秒完成,这意味着每个动作都在 之后 处理完珍贵的动作。因为 setTimeoutsetInterval 产生的动作放在 Queue 上,毫秒数 不是调用相应函数的保证时间 .这是一个保证的最短时间,在它们被执行之前经过。这使得它们都产生 异步操作 .

但是,从体系结构 的角度来看,您需要的是一种可靠且可扩展的方式来排序异步操作。这就是 Promise 发挥作用的地方。

稍微挥手,我们可以说我们可以通过使用回调函数而不是Promise来达到相同的解决方案,因为事件循环的工作方式.它已经一次调用一个函数,对吧?所以这是一个基于回调的 "equal" 解决方案:

// 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>

它工作得同样好,并允许您以可靠的方式链接您的操作。它的缺点是:

  • 它的扩展性不是很好。要链接更多操作,您需要更深的嵌套,因为必须在成功回调函数中调用新操作。
  • 您必须手动嵌套。因此,嵌套得越深,跟踪代码流就越难。这就是为什么人们称它为 "pyramid of doom"。想象一下必须混合 20 个元素的文本。
  • 看看它的签名:showTextAfter :: Number -> String -> String -> Function -> Function -> undefined。对于那个微不足道的小功能来说,这是很多东西!只传递前 3 个参数不是很酷吗?

我们可以通过 return 从对 showTextAfter 的调用中调用一个新函数来稍微缓解最后一个问题,它会消耗 onCompleteonError 回调:

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 解决的问题!它们允许您以 可扩展的方式 对异步操作 进行排序,并且更容易跟踪 控制流 。上面的所有问题都可以通过具有(本机)"in place" return 值来消除,您可以 "chain" 其他异步步骤将完成 "in the future" (这意味着它们可以成功完成或者他们可以用 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>