I am writing a browser-based javascript to scrape the input value by user and provide feedback. And in the page, you have to click on different areas for the desired element to show. I am trying to see if there is a way I can get javascript to wait until the element returns before I start going to the next one. Right now I just use setTimeout, but I have to loop all the elements I want to get, so nasty nested functions. trying to see if there is a more elegant solution. I used to write VBA, so I am not so familiar with async web API. still learning
function a(){
document.getElementById('1').click()
setTimeout(function(){document.getElementById('1a');addmessage;b;}),5000}
}
function b(){
document.getElementById('2').click()
setTimeout(function(){document.getElementById('2a');addmessage;c;}),5000}
}
function c(){
document.getElementById('3').click()
setTimeout(function(){document.getElementById('3a');addmessage;d;}),5000}
}
function addmessage(){
//this is the function I used to add element do some operation and add to dialog box
}
etc..
what I imagined is like below
function addmessage(element_to_click,element_to_collect){
// click element_to_click
// wait 5s
// collect innertext from element_to_collect
// do some operation, generate a message
//add message to dialog box
}
so I can do something like
addmessage(1,1a)
addmessage(2,2a) <--- this won't execute until the first is complete.
CodePudding user response:
If I'm understanding the question correctly, you can start with a promise wrapper around setTimeout
:
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
Then addMessage
could be:
async function addMessage(element_to_click, element_to_collect) {
element_to_click.click();
await delay(5000);
return element_to_collect.textContent;
}
You didn't say what you wanted to do with the text, so I've just had it return it.
Then you can either write a loop or a series of calls:
async function run() {
const a = await addMessage(
document.getElementById("1"),
document.getElementById("???"),
);
const b = await addMessage(
document.getElementById("2"),
document.getElementById("???"),
);
const c = await addMessage(
document.getElementById("2"),
document.getElementById("???"),
);
// ...do something with `a`, `b`, and `c`...
}
When calling run
, don't forget to handle promise rejection (in case something goes wrong).
You've said that after the click, "...the page needs a few seconds to load..." Hardcoded timeouts are fragile and often break. Instead of a timeout, try to hook into something that will change, perhaps via a MutationObserver
on the element you're "collecting" from.
CodePudding user response:
You can make your code wait for a timeout to complete by wrapping it in a Promise
and using the async await
pattern:
// Return a new Promise immediately, that completes at the end of the timeout
function addmessage(element_to_click, element_to_collect) {
return new Promise((resolve, reject) => {
// do whatever
// resolve the Promise after 5s
setTimeout(resolve, 5000)
// you can also resolve the Promise on any other event, like user input
// (or use `reject` when an error occurs)
// you can pass a return value to `resolve` like resolve(input.value)
})
}
// `async` functions also return a Promise immediately
async function addMessages() {
// `await` will block until Promise completes and
// returns the resolved value to be used synchronously within the async func
const in1 = await addmessage('1', '1a')
const in2 = await addmessage('2', '2a')
const in3 = await addmessage('3', '3a')
}
addMessages()