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);
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 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();
});
}
Not an entirely serious question, more of a shower thought: JavaScript's await keyword should allow for something that feels an awful lot like a mutex in your average "concurrent language".
function Mutex() {
var self = this; // still unsure about how "this" is captured
var mtx = new Promise(t => t()); // fulfilled promise ≡ unlocked mutex
this.lock = async function() {
await mtx;
mtx = new Promise(t => {
self.unlock = () => t();
});
}
}
// Lock
await mutex.lock();
// Unlock
mutex.unlock();
Is this a correct implementation (apart from proper error handling)? And… can I have C++-RAII-style lock guards?
Your implementation allows as many consumers obtain the lock as ask for it; each call to lock waits on a single promise:
function Mutex() {
var self = this; // still unsure about how "this" is captured
var mtx = new Promise(t => t()); // fulfilled promise ≡ unlocked mutex
this.lock = async function() {
await mtx;
mtx = new Promise(t => {
self.unlock = () => t();
});
}
}
const mutex = new Mutex();
(async () => {
await Promise.resolve();
await mutex.lock();
console.log("A got the lock");
})();
(async () => {
await Promise.resolve();
await mutex.lock();
console.log("B got the lock");
})();
You'd need to implement a queue of promises, creating a new one for each lock request.
Side notes:
new Promise(t => t()) can be more simply and idiomatically written Promise.resolve() :-)
No need for self if you're using arrow functions like that; arrow functions close over the this where they're created (exactly like closing over a variable)
Probably would make sense for unlock to be a resolution value of the lock promise, so only the code that obtained the lock can release it
Something like this:
function Mutex() {
let current = Promise.resolve();
this.lock = () => {
let _resolve;
const p = new Promise(resolve => {
_resolve = () => resolve();
});
// Caller gets a promise that resolves when the current outstanding
// lock resolves
const rv = current.then(() => _resolve);
// Don't allow the next request until the new promise is done
current = p;
// Return the new promise
return rv;
};
}
Live Example:
"use strict";
function Mutex() {
let current = Promise.resolve();
this.lock = () => {
let _resolve;
const p = new Promise(resolve => {
_resolve = () => resolve();
});
// Caller gets a promise that resolves when the current outstanding
// lock resolves
const rv = current.then(() => _resolve);
// Don't allow the next request until the new promise is done
current = p;
// Return the new promise
return rv;
};
}
const rand = max => Math.floor(Math.random() * max);
const delay = (ms, value) => new Promise(resolve => setTimeout(resolve, ms, value));
const mutex = new Mutex();
function go(name) {
(async () => {
console.log(name + " random initial delay");
await delay(rand(50));
console.log(name + " requesting lock");
const unlock = await mutex.lock();
console.log(name + " got lock");
await delay(rand(1000));
console.log(name + " releasing lock");
unlock();
})();
}
go("A");
go("B");
go("C");
go("D");
.as-console-wrapper {
max-height: 100% !important;
}
Is this a correct implementation?
No. If two tasks (I can't say "threads") try to do mutex.lock() while it is currently locked, they will both get the lock at the same time. I doubt that's what you want.
A mutex in JS is really just a boolean flag - you check it, you set it when you acquire a lock, you clear it when you release the lock. There's no special handling of race conditions between checking and acquiring as you can do it synchronously in single-threaded JS, without any other threads interfering.
What you seem to be looking for however is a queue, i.e. something where you can schedule yourself to get the lock and will be notified (through a promise) when the previous lock is released.
I'd do that with
class Mutex {
constructor() {
this._lock = null;
}
isLocked() {
return this._lock != null;
}
_acquire() {
var release;
const lock = this._lock = new Promise(resolve => {
release = resolve;
});
return () => {
if (this._lock == lock) this._lock = null;
release();
};
}
acquireSync() {
if (this.isLocked()) throw new Error("still locked!");
return this._acquire();
}
acquireQueued() {
const q = Promise.resolve(this._lock).then(() => release);
const release = this._acquire(); // reserves the lock already, but it doesn't count
return q; // as acquired until the caller gets access to `release` through `q`
}
}
Demo:
class Mutex {
constructor() {
this._lock = Promise.resolve();
}
_acquire() {
var release;
const lock = this._lock = new Promise(resolve => {
release = resolve;
});
return release;
}
acquireQueued() {
const q = this._lock.then(() => release);
const release = this._acquire();
return q;
}
}
const delay = t => new Promise(resolve => setTimeout(resolve, t));
const mutex = new Mutex();
async function go(name) {
await delay(Math.random() * 500);
console.log(name + " requests lock");
const release = await mutex.acquireQueued();
console.log(name + " acquires lock");
await delay(Math.random() * 1000);
release()
console.log(name + " releases lock");
}
go("A");
go("B");
go("C");
go("D");
I recommend using a library like async-mutex:
const mutex = new Mutex();
// ...
const release = await mutex.acquire();
try {
// ...
} finally {
release();
}
For the following function, I have to add a timeout after every GET request in array ajaxUrls. All the XHR GET request are in array ajaxUrls.
function getAllSearchResultProfiles(searchAjaxUrl) {
var ajaxUrls = [];
for (var i = 0; i < numResults; i += resultsPerPage) {
ajaxUrls.push(searchAjaxUrl + "&start=" + i);
}
return Promise.all(ajaxUrls.map(getSearchResultsForOnePage))
.then(function(responses) {
return responses.map(function(response) {
if (response.meta.total === 0) {
return [];
}
return response.result.searchResults.map(function(searchResult) {
return (searchResult);
});
});
})
.then(function(searchProfiles) {
return [].concat.apply([], searchProfiles);
})
.catch(function(responses) {
console.error('error ', responses);
});
}
function getSearchResultsForOnePage(url) {
return fetch(url, {
credentials: 'include'
})
.then(function(response) {
return response.json();
});
}
I want a certain timeout or delay after every GET request. I am facing difficulty in where exactly to add the timeout.
If you want to make requests in serial, you shouldn't use Promise.all, which initializes everything in parallel - better to use a reduce that awaits the previous iteration's resolution and awaits a promise-timeout. For example:
async function getAllSearchResultProfiles(searchAjaxUrl) {
const ajaxUrls = [];
for (let i = 0; i < numResults; i += resultsPerPage) {
ajaxUrls.push(searchAjaxUrl + "&start=" + i);
}
const responses = await ajaxUrls.reduce(async (lastPromise, url) => {
const accum = await lastPromise;
await new Promise(resolve => setTimeout(resolve, 1000));
const response = await getSearchResultsForOnePage(url);
return [...accum, response];
}, Promise.resolve([]));
// do stuff with responses
const searchProfiles = responses.map(response => (
response.meta.total === 0
? []
: response.result.searchResults
));
return [].concat(...searchProfiles);
}
Note that only asynchronous operations should be passed from one .then to another; synchronous code should not be chained with .then, just use variables and write the code out as normal.
I find a simple for loop in an async function to be the most readable, even if not necessarily the most succinct for things like this. As long as the function is an async function you can also create a nice pause() function that makes the code very easy to understand when you come back later.
I've simplified a bit, but this should give you a good idea:
function pause(time) {
// handy pause function to await
return new Promise(resolve => setTimeout(resolve, time))
}
async function getAllSearchResultProfiles(searchAjaxUrl) {
var ajaxUrls = [];
for (var i = 0; i < 5; i++) {
ajaxUrls.push(searchAjaxUrl + "&start=" + i);
}
let responses = []
for (url of ajaxUrls) {
// just loop though and await
console.log("sending request")
let response = await getSearchResultsForOnePage(url)
console.log("recieved: ", response)
responses.push(response)
await pause(1000) // wait one second
}
//responses.map() and other manilpulations etc...
return responses
}
function getSearchResultsForOnePage(url) {
//fake fetch
return Promise.resolve(url)
}
getAllSearchResultProfiles("Test")
.then(console.log)
If you want to add a delay in every request then add a setTimout() in your function which fetches data from api
function getSearchResultsForOnePage(url) {
return new Promise((resolve, reject) => {
fetch(url, {
credentials: 'include'
})
.then(response => reresponse.json())
.then(data => {
let timeout = 1000;
setTimeout(() => resolve(data), timeout);
});
}