I have a userscript which loads a list of buttons on a page. It then tries to click all the buttons one by one in a loop. This is my function.
const clickButtons = async function(listButtons) {
const filteredButtons = Array.from(listButtons).filter(x => shouldButtonBeClicked(x.innerText));
for (let i = 0; i < filteredButtons.length; i++) {
filteredButtons[i].click();
}
}
The above piece of code works as expected. No issues. All the buttons are clicked.
But, when I try to add some wait time before every click, it doesn't work. None of the buttons get clicked. Notice wait() on first line inside the loop
const wait = async function(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
const clickButtons = async function(listButtons) {
const filteredButtons = Array.from(listButtons).filter(x => shouldButtonBeClicked(x.innerText));
for (let i = 0; i < filteredButtons.length; i++) {
await wait(1000);
filteredButtons[i].click();
}
}
What am I missing here?
Resolving the promise with a click should work
await wait(1000).then((res)=> filteredButtons[i].click());
You can try this:
const wait = async function(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
const clickButtons = async function(listButtons) {
const filteredButtons = Array.from(listButtons).filter(x => shouldButtonBeClicked(x.innerText));
filteredButtons.forEach(async function (filteredButton, index) {
await wait(1000);
filteredButtons[index].click();
// OR
// filteredButton.click();
});
}
Related
I'm trying to show the process of how the backtracking algorithm solves a sudoku board, but I'm not sure how I can make sure that it only gets called every 500ms.
function solveBoard(board) {
// I tried doing setTimeout from here to the bottom, but it breaks the solver and just puts 9s everywhere.
let empty = findEmpty(board);
if (!empty) return true;
let row = empty[0];
let col = empty[1];
for (let i = 1; i < 10; i++) {
board[row][col] = i;
console.log(board[row][col]);
document.getElementById(`${row}-${col}`).value = i;
if (checkValid(board, row, col)) {
if (solveBoard(board)) {
return true;
}
}
board[row][col] = 0;
}
return false;
}
The first time I call solve board is just an event listener.
solveBtn.addEventListener("click", () => {
solveBoard(boardArray);
});
Call sleep in solveBoard
async function solveBoard(board) {
await sleep()
// …
}
function sleep(ms = 500) {
return new Promise(resolve => setTimeout(resolve, ms))
}
// Demo
(async () => {
console.log('a')
await sleep()
console.log('b')
await sleep()
console.log('c')
})();
my function greetings works but when i try to return a promise to execute things once its done im getting nothing, what am i missing?
I have tried putting return resolve() as to make sure the function ends but still nothing, I can´t get the .then() to execute.
const greetingDivs = [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv]
let y = 0
function greetings() {
return new Promise((resolve, reject) => {
if (y == greetingDivs.length) {
// console.log('resolved')
resolve('done')
}
if (y != greetingDivs.length) {
setTimeout(() => {
let lastDiv = consoleOutput.appendChild(greetingDivs[y])
.scrollIntoView(false, { behavior: 'smooth' })
y++
greetings()
}, 300)
}
})
}
greetings().then(() => {
console.log('hello')
})
Your code only resolves one promise, while it creates 5. Notably, the first one, the one that greetings() returns, never resolves.
I would suggest promisifying setTimeout, so you have that logic once and for all. Then the looping can be done in an async function with a for loop and await:
const consoleOutput = document.body;
const [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv] = [
"Hello", "Welcome to this demo", "Hope it suits your case", "Test it", "and enjoy"].map(s => {
const div = document.createElement("div");
div.textContent = s;
return div;
});
const greetingDivs = [firstDiv, secondDiv, thirdDiv, fourthDiv, fifthDiv];
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
async function greetings() {
for (let div of greetingDivs) {
consoleOutput.appendChild(div)
.scrollIntoView(false, { behavior: 'smooth' });
await delay(300);
}
}
greetings().then(() => {
console.log('hello');
});
See ES6 promise for loop for more ideas on how you can create such loops.
I am ordering some items with their priorities. I used a loop for that. However, I get some weird outputs like [1,1,2,2,3] instead of [1,2,3,4,5](these are priorities btw). The loop is below.
const switchPriority = async function(catId, srcI, destI){
let indexofCat;
try {
for (let i = 0; i < data[0].length; i++) {
const element = data[0][i];
if(element.id === catId){
indexofCat = i;
}
}
let Item = Parse.Object.extend('Item')
let itemQuery = new Parse.Query(Item)
for (let i = (srcI>destI?destI:srcI); i < (srcI>destI?(srcI+1):(destI+1)); i++) {
let id = data[1][indexofCat][i].id;
let item = await itemQuery.get(id);
item.set("priority",i+1);
await item.save();
}
} catch (error) {
alert(error)
}
}
Why there is such a problem? When I add alert to the loop, with some delay it gives proper outputs. How can I solve this ? I am appreciate for your help.
I don't know if you forgot, but it's necessary use the word async like this await works. You can insert your code into a function:
async function parse(){
for (let i = (srcI); i < (destI); i++) {
// alert("index in loop "+ i);
let id = data[1][indexofCat][i].id;
let item = await itemQuery.get(id);
// alert(item.get("name"));
item.set("priority",i+1);
await item.save();
}
}
Look at for more details: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Statements/async_function
You can also promisify a function that you needs to guarantee that it be executed before the next instruction. How?
function createPromise (parameter) {
return new Promise((resolve, reject) => {
resolve(console.log(parameter))
})
}
async function test(){
console.log('First')
await createPromise('Promise')
console.log('Second')
}
test()
Look at: https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise
I have to keep making calls to SERVICE_PATH until the report in the response ready.
Apparently, below code is blocking due to the usage of "do/while".
So, the question is how can I restructure the code to make it non-blocking?
let scoring_report;
let timeout_count = 1;
let getReport;
do {
await new Promise((resolve) => setTimeout(resolve, timeout_count * 1000));
getReport = await axios.post(`${process.env.SERVICE_PATH}:8001`, {body});
scoring_report = getReport.data.data.reportBase64;
timeout_count += 2;
} while (!scoring_report);
Your code is blocking not due to do... while... Its due to the async await.
Sample blocking code.
async function resolveAfterSomeTime() {
let counter = 0;
do {
console.log("starting promise")
const x = await new Promise(resolve => {
setTimeout(function () {
resolve("done")
console.log("promise is done")
}, 800)
});
counter++;
console.log(x);
} while (counter < 5);
}
resolveAfterSomeTime();
As you can see the above code is blocking for 800 milli second for each execution due to async await.
You can make this non blocking by simply removing async await. If you are not interested in the result, you can simply call the function and go for the next iterearion. Here in the below code, the value for x will be a promise, you can check the resolved status of the promise to handle your further logic using x.then((resp) => console.log(resp))
Sample code.
function resolveAfterSomeTime() {
let counter = 0;
do {
console.log("starting promise")
const x = new Promise(resolve => {
setTimeout(function () {
console.log("promise is done");
resolve("done");
}, 800)
});
counter++;
x.then((resp) => console.log(resp))
} while (counter < 5);
}
resolveAfterSomeTime();
Your sample non blocking code
let scoring_report;
let timeout_count = 1;
let getReport;
do {
new Promise((resolve) => setTimeout(resolve, timeout_count * 1000));
// here getReport will be a promise
getReport = axios.post(`${process.env.SERVICE_PATH}:8001`, { body });
// Use getReport.then((data) => {}) for further logic
} while (!scoring_report);
Implementation using setInterval
If you just want to execute the function on the specified interval, you can make use of setInterval which will not block your main thread. Donot forget to clearInterval once the required result is available.
Sample Implementation
let scoring_report;
let timeout_count = 1;
let getReport;
const myInterval = setInterval(() => {
getReport = await axios.post(`${process.env.SERVICE_PATH}:8001`, { body });
scoring_report = getReport.data.data.reportBase64;
timeout_count += 2;
if(scoring_report) {
clearInterval(myInterval)
}
}, timeout_count * 1000);
I am trying to append paragraph's to a container using recursive function.
The code looks like this -
loadSections (sections, buttons) {
let sectionLength = sections.length;
let self = this;
this.sleep(sections[sectionCounter].DelayInMs);
let context = {
message: sections[sectionCounter].Text,
};
this.fillSections(context, function () {
sectionCounter ++;
if (sectionCounter < sectionLength) {
self.loadSections(sections, buttons);
}
});
}
fillSections (context, callback) {
let messageList = $(document.createElement('p'));
messageList.html(context.message);
$('.chat-flow-container').append(messageList);
callback();
}
sleep (milliSeconds) {
let start = new Date().getTime();
let expire = start + milliSeconds;
while (new Date().getTime() < expire) { }
return;
}
The code just works. But the issue is all appended p elements into .chat-flow-container are not shown with the delay with I mentioned in sleep method, instead they all are shown together once the recursion ends.
The busy loop in the sleep method is preventing your browser from updating the screen. It blocks the browser. So this is not a good way to proceed.
Instead make your code asynchronous. If you have support for async/await then you could do it like this:
async loadSections(sections, buttons) {
for (let section of sections) {
await this.sleep(section.DelayInMs);
let context = {
message: section.Text,
};
this.fillSections(context);
}
}
fillSections (context) {
let messageList = $(document.createElement('p'));
messageList.html(context.message);
$('.chat-flow-container').append(messageList);
}
sleep (milliSeconds) {
return new Promise(resolve => setTimeout(resolve, milliSeconds));
}
Now loadSections will return immediately, but the code in it will still resume later when the setTimeout expires.
const sections = [
{ DelayInMs: 300, Text: 'A' },
{ DelayInMs: 1000, Text: 'B' },
{ DelayInMs: 1000, Text: 'C' },
];
class X {
async loadSections(sections, buttons) {
for (let section of sections) {
await this.sleep(section.DelayInMs);
let context = {
message: section.Text,
};
this.fillSections(context);
}
}
fillSections (context) {
let messageList = $(document.createElement('p'));
messageList.html(context.message);
$('.chat-flow-container').append(messageList);
}
sleep (milliSeconds) {
return new Promise(resolve => setTimeout(resolve, milliSeconds));
}
}
new X().loadSections(sections);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="chat-flow-container"></div>