我想在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秒钟,然后更新我的div。
我希望我的代码:
等待1秒钟, 更新div, 等一秒钟 更新div, 等一秒钟 更新div
答案 0 :(得分:1)
您可以尝试使用setTimeout。
我做了这个功能,可以很容易地提供要更新的对象数组。
.as-console-wrapper { max-height: 100% !important; top: 0; }
样品用量:
/*
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();
,然后在1000毫秒(1秒)后调用one()
,然后在1000毫秒(1秒)后调用two()
。
答案 1 :(得分:1)
Promise
可以解决:
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
是正确的解决方案的原因最终植根于JavaScript的运行时概念。简而言之,这是因为从技术上讲setTimeout
和setInterval
都是异步动作,因为它们都由JavaScript event loop 处理。在MDN上可以找到事件循环和JavaScript的一般并发模型的详尽解释。
简而言之:必须执行的每个动作都被推送到运行时必须运行的Queue
个动作的末尾,好吧,运行。这样,可以处理来自UI的动作以及其他动作,例如超时和间隔。运行时会逐步处理这些操作,但是它们可能需要不同的时间才能完成。这是因为运行时会运行到完成状态,这意味着每个操作都在 之后被处理,而宝贵的操作已被完全处理。由于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>
它同样运作良好,并允许您以可靠的方式链接操作。缺点是:
showTextAfter :: Number -> String -> String -> Function -> Function -> undefined
。那个微不足道的功能有很多东西!仅传递前三个参数不是很酷吗?我们可以通过从调用返回一个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
解决的问题!它们允许您以可伸缩的方式 序列异步操作,并且更容易跟踪控制流。可以通过(本地)返回值(原位)返回值来消除上述所有问题,您可以将这些返回值“链接”到其他将在“将来”完成的异步步骤(这意味着它们可以成功完成,也可以使用{ {1}})。真正聪明的是,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'));
答案 2 :(得分:0)
您可以利用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();