为什么这个函数不起作用(等待 Javascript 中的元素)?
Why doesn't this function work (waiting for an element in Javascript)?
基本上,我们有一个 setTimeout()
函数,它在创建元素并将其附加到正文之前等待半秒。然后我们有一个名为 foo()
的异步函数,它尝试定位该元素 (while(!expression)
) 并在找不到它时等待承诺。承诺只等待十分之一秒并解决。
我设想的这种工作方式是 while 循环运行的前五次,元素未找到并且 promise 已解决。然后在第六次,顶部的 setTimeout()
函数创建元素,while 循环表达式解析为 true。这意味着到达 foo().then()
表达式中的 console.log()
(并且代码可以继续。
我认为这是时间问题。当创建元素的代码从 setTimeout()
函数中取出时,代码执行得很好,所以看起来 while 循环在 top setTimeout()
函数可以触发之前无限期地 运行 。为什么是这样?有没有办法在不重写所有代码的情况下解决这个问题?似乎这个 foo()
函数,我在一个更大的代码库中,最近停止工作了。浏览器解析的方式是否发生了变化 Javascript?
注意:foo()
背后的想法是我在 then()
函数中编写的内容应始终等待创建由 expression
标识的元素。
setTimeout(function () {
const el = document.createElement('div')
el.id = 'bar'
el.innerText = 'Bar'
document.body.appendChild(el)
}, 500)
const promise = new Promise(function(resolve) {
setTimeout(function() {
resolve()
}, 100)
})
async function foo(expression) {
while(!expression) {
await promise
}
}
foo(document.getElementById('bar')).then(function() {
console.log(document.getElementById('bar'))
})
有些不对的地方
首先是:
async function foo(expression) {
while(!expression) {
await promise
}
}
永远不会做任何有用的事情。如果您为 expression
传递真值,则循环永远不会 运行。如果你为 expression
传递了一个错误的值,那么循环将是一个无限循环,循环永远不会停止,因为循环中没有代码会改变 expression
的值。
另请注意,expression
是作为一个值传递的,而不是一个可以再次调用的函数,因此它在传递时只有一个值,并且该值在该函数内永远不会改变。因此,使用 while()
条件对其进行测试实际上也没有任何用处。
此外,await promise
将始终等待相同的承诺,因此它会在第一次延迟,但该承诺将在后续时间得到解决,因此不会增加任何延迟。 await
使用已解决的承诺不会再延迟。
然后,这个:
foo(document.getElementById('bar')).then(function() {
console.log(document.getElementById('bar'))
})
总是将 document.getElementById('bar')
的结果传递给 foo()
,这将是真实的或虚假的,这取决于文档中是否存在 #bar
元素。如果它是真实的,因为它存在,那么 foo()
将立即解决它的承诺,因为它永远不会进入 while
循环。如果 #bar
不存在,那么 foo()
将永远处于无限循环中。
使用 setInterval 实现
如果您试图“轮询”某个元素何时存在,请仅使用 setInterval()
或使用计数器来限制它 运行 的长度。它简单多了,不会受到任何这些问题的影响。
let cntr = 0;
const maxChecks = 100;
let timer = setInterval(() => {
++cntr;
let item = document.getElementById('bar');
if (item) {
// found #bar
clearInterval(timer);
// do something with #bar
} else {
if (cntr > maxChecks) {
clearInterval(timer);
console.log("gave up checking for #bar");
}
}
}, 100);
实现使用带有 await 的 promise
如果你真的想使用 promise,你可以这样做:
function delay(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
async function waitForElement(id, maxWaitTime = 2000) {
const start = Date.now();
while (Date.now() - start < maxWaitTime) {
let item = document.getElementById(id);
if (item) {
return item;
} else {
// wait a little and try again
await delay(100);
}
}
throw new Error("Element doesn't exist after max time");
}
这为您提供了一个函数,该函数等待(最长时间)元素存在,然后解析或拒绝返回的承诺。
waitForElement("bar").then(elem => {
console.log("element exists");
console.log(elem.innerHTML); // show contents of DOM element
}).catch(err => {
console.log("element does not exist after max time");
});
可运行代码段
这里是 运行 可用代码段中的工作版本:
// create #bar element after a bit of time
setTimeout(function () {
const el = document.createElement('div')
el.id = 'bar'
el.innerText = 'Bar exists now'
document.body.appendChild(el)
}, 500);
function delay(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
async function waitForElement(id, maxWaitTime = 2000) {
const start = Date.now();
while (Date.now() - start < maxWaitTime) {
let item = document.getElementById(id);
if (item) {
return item;
} else {
// wait a little and try again
await delay(100);
}
}
throw new Error("Element doesn't exist after max time");
}
// wait for #bar to exist and output it's contents
waitForElement("bar").then(elem => {
console.log("element exists now and its content is '" + elem.innerHTML + "'");
}).catch(err => {
console.log("element does not exist after max time");
})
问题是 document.getElementById('bar')
的值被评估一次,结果存储在 expression
变量中。要使其在 while 循环中每次都求值,您需要传递一个将导致求值的回调,然后每次都调用它:
async function foo(callback) {
while(!callback()) {
await promise
}
}
foo(() => document.getElementById('bar')).then(function() {
console.log(document.getElementById('bar'))
})
请注意,我想帮助您解决这个具体问题并向您展示哪里出了问题,但我绝不是说在 DOM 上轮询查询是个好主意。
Puppeteer提供了三种轮询的方式来等待一个元素出现在DOM:
waitForPredicatePageFunction
将 运行 在 puppeteer 之外,没有其他 Puppeteer 依赖项。还有各种types.
的好处的简单说明
一个 promise 只能解决一次,所以如果你重复使用同一个 promise,只有第一次 500 毫秒的等待被兑现。
基本上,我们有一个 setTimeout()
函数,它在创建元素并将其附加到正文之前等待半秒。然后我们有一个名为 foo()
的异步函数,它尝试定位该元素 (while(!expression)
) 并在找不到它时等待承诺。承诺只等待十分之一秒并解决。
我设想的这种工作方式是 while 循环运行的前五次,元素未找到并且 promise 已解决。然后在第六次,顶部的 setTimeout()
函数创建元素,while 循环表达式解析为 true。这意味着到达 foo().then()
表达式中的 console.log()
(并且代码可以继续。
我认为这是时间问题。当创建元素的代码从 setTimeout()
函数中取出时,代码执行得很好,所以看起来 while 循环在 top setTimeout()
函数可以触发之前无限期地 运行 。为什么是这样?有没有办法在不重写所有代码的情况下解决这个问题?似乎这个 foo()
函数,我在一个更大的代码库中,最近停止工作了。浏览器解析的方式是否发生了变化 Javascript?
注意:foo()
背后的想法是我在 then()
函数中编写的内容应始终等待创建由 expression
标识的元素。
setTimeout(function () {
const el = document.createElement('div')
el.id = 'bar'
el.innerText = 'Bar'
document.body.appendChild(el)
}, 500)
const promise = new Promise(function(resolve) {
setTimeout(function() {
resolve()
}, 100)
})
async function foo(expression) {
while(!expression) {
await promise
}
}
foo(document.getElementById('bar')).then(function() {
console.log(document.getElementById('bar'))
})
有些不对的地方
首先是:
async function foo(expression) {
while(!expression) {
await promise
}
}
永远不会做任何有用的事情。如果您为 expression
传递真值,则循环永远不会 运行。如果你为 expression
传递了一个错误的值,那么循环将是一个无限循环,循环永远不会停止,因为循环中没有代码会改变 expression
的值。
另请注意,expression
是作为一个值传递的,而不是一个可以再次调用的函数,因此它在传递时只有一个值,并且该值在该函数内永远不会改变。因此,使用 while()
条件对其进行测试实际上也没有任何用处。
此外,await promise
将始终等待相同的承诺,因此它会在第一次延迟,但该承诺将在后续时间得到解决,因此不会增加任何延迟。 await
使用已解决的承诺不会再延迟。
然后,这个:
foo(document.getElementById('bar')).then(function() {
console.log(document.getElementById('bar'))
})
总是将 document.getElementById('bar')
的结果传递给 foo()
,这将是真实的或虚假的,这取决于文档中是否存在 #bar
元素。如果它是真实的,因为它存在,那么 foo()
将立即解决它的承诺,因为它永远不会进入 while
循环。如果 #bar
不存在,那么 foo()
将永远处于无限循环中。
使用 setInterval 实现
如果您试图“轮询”某个元素何时存在,请仅使用 setInterval()
或使用计数器来限制它 运行 的长度。它简单多了,不会受到任何这些问题的影响。
let cntr = 0;
const maxChecks = 100;
let timer = setInterval(() => {
++cntr;
let item = document.getElementById('bar');
if (item) {
// found #bar
clearInterval(timer);
// do something with #bar
} else {
if (cntr > maxChecks) {
clearInterval(timer);
console.log("gave up checking for #bar");
}
}
}, 100);
实现使用带有 await 的 promise
如果你真的想使用 promise,你可以这样做:
function delay(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
async function waitForElement(id, maxWaitTime = 2000) {
const start = Date.now();
while (Date.now() - start < maxWaitTime) {
let item = document.getElementById(id);
if (item) {
return item;
} else {
// wait a little and try again
await delay(100);
}
}
throw new Error("Element doesn't exist after max time");
}
这为您提供了一个函数,该函数等待(最长时间)元素存在,然后解析或拒绝返回的承诺。
waitForElement("bar").then(elem => {
console.log("element exists");
console.log(elem.innerHTML); // show contents of DOM element
}).catch(err => {
console.log("element does not exist after max time");
});
可运行代码段
这里是 运行 可用代码段中的工作版本:
// create #bar element after a bit of time
setTimeout(function () {
const el = document.createElement('div')
el.id = 'bar'
el.innerText = 'Bar exists now'
document.body.appendChild(el)
}, 500);
function delay(t) {
return new Promise(resolve => setTimeout(resolve, t));
}
async function waitForElement(id, maxWaitTime = 2000) {
const start = Date.now();
while (Date.now() - start < maxWaitTime) {
let item = document.getElementById(id);
if (item) {
return item;
} else {
// wait a little and try again
await delay(100);
}
}
throw new Error("Element doesn't exist after max time");
}
// wait for #bar to exist and output it's contents
waitForElement("bar").then(elem => {
console.log("element exists now and its content is '" + elem.innerHTML + "'");
}).catch(err => {
console.log("element does not exist after max time");
})
问题是 document.getElementById('bar')
的值被评估一次,结果存储在 expression
变量中。要使其在 while 循环中每次都求值,您需要传递一个将导致求值的回调,然后每次都调用它:
async function foo(callback) {
while(!callback()) {
await promise
}
}
foo(() => document.getElementById('bar')).then(function() {
console.log(document.getElementById('bar'))
})
请注意,我想帮助您解决这个具体问题并向您展示哪里出了问题,但我绝不是说在 DOM 上轮询查询是个好主意。
Puppeteer提供了三种轮询的方式来等待一个元素出现在DOM:
waitForPredicatePageFunction
将 运行 在 puppeteer 之外,没有其他 Puppeteer 依赖项。还有各种types.
一个 promise 只能解决一次,所以如果你重复使用同一个 promise,只有第一次 500 毫秒的等待被兑现。