How can I call method with queue?
Let say I have a method, this method will call the API and it needs to handle only 3 call at a time. When more than 3 calls from some component. It needs to wait until any of the call finish and run the next
This is just illustration, I don't loop API call, the real project is call API by user action
for (let i = 0; i < 9; i++) {
doSomething(i)
}
doSomething(param){
//call api
}
It should call doSomething 0 - 9, but I want to call api for the first 3, and the other will wait until any of the call is finished
Try using Promise and async functions for the implementation.
A sample implementation.
for (let i = 0; i < 9; i++) {
doSomething(i)
}
async function doSomething(param) {
//call api
if ( param < 3 ) {
const x = callApi();
console.log(x);
} else {
const x = await callApi();
console.log(x);
}
}
async function callApi() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000)
})
}
Related
So, i have a function in file that converts into bundler.js via webpack (devtool: 'eval-source-map'):
function getTable(tname, trowid) {
return get(ref(db, `table_name/${tname}/table_row_${String("0" + trowid).slice(-2)}`)).then((snapshot) => {
if (snapshot.exists()) {
console.log(snapshot.val()); // need to return this value
}
});
}
window.getTable = getTable;
and i need call this function in main.js smt like this:
for (i = 0; i < 69; i++) {
document.getElementById(i).value = window.getTable(tname, i);
}
I'd tried simply return this vale, do it global variable and more, but in result i'm still getting "undefined".
Instead of logging your value, return it to the Promise your getTable() function is returning with return snapshot.val() :
if (snapshot.exists()) {
return snapshot.val();
}
This makes it so getTable() returns a Promise that fulfills with the value that snapshot.val() returns. Now you have a few options. One is to use async/await on each promise returned by the getTable() function to "extract" the value from the Promise:
async function populateValues() {
for (let i = 0; i < 69; i++) {
document.getElementById(i).value = await getTable(tname, i);
}
}
populateValues();
This will call each getTable function one at a time, perform the asynchronous operation/code within it, wait for that to complete (ie: wait for the Promise getTable() returns to fulfill with a value), then update your element's .value. Once done, it will proceed to the next iteration, and it will do this 69 times. For something faster, you can kick off all your calls to getTable() in parallel, and then use Promise.all() to wait until all of them have been fulfilled:
async function populateValues() {
const promises = [];
for (let i = 0; i < 69; i++) {
promises.push(getTable(tname, i)); // kick off all your asynchronous calls (so that they run in parallel)
}
const tableValues = await Promise.all(promises); // wait for each async operation to finish, then iterate the data and update your UI
for(const [i, val] of tableValuese.entries()) { // you can use a standard `for` loop here to iterate your `tableValues` array if you'd like
document.getElementById(i).value = val;
}
}
populateValues();
I have two for loop, inside them I have setTimeout function like following code:
for(let i=0;i<3;i++){
setTimeout(()=>{console.log(i)}, 1000)
}
for(let i=0;i<3;i++){
setTimeout(()=>{console.log(i)}, 1000)
}
i want the second loop does not executed until after the first loop finished,
I want this result:
0
1
2
0
1
2
- and between 2 numbers wait 1 second.
how i can do that?
I can't comment yet or I would ask clarifying questions so I'll give you what I think you're asking for. If you want a 1 second delay between each number being logged to the console then this will work:
const func = async () => {
for (let i = 0; i < 3; i++) {
await new Promise((resolve) =>
setTimeout(() => {
console.log(i);
resolve();
}, 1000)
);
}
for (let i = 0; i < 3; i++) {
await new Promise((resolve) =>
setTimeout(() => {
console.log(i);
resolve();
}, 1000)
);
}
};
func();
A quick rundown of what's happening here. I created an outer function named func which I made asynchronous so that I can use the await keyword within it. I then put the setTimeout call inside a new instance of Promise. The promise combined with the await means that javascript will essentially stop at that line until the Promise calls resolve. Once resolve is called that instance of Promise is finished and the await statement stops "blocking" javascript and the callbackque continues.
TLDR:
To use await you must be in an asynchronous function.
If await is used in front of a Promise everything will stop until the promise resolves.
Placing the resolve of the promise inside the callback given to setTimeout ensures that we will wait until each timeout finishes BEFORE the next timeout begins.
This will work
nxt (0, 0);//seed the stack
function nxt(num, itertn){
if(num == 3){//want till 2
if(itertn == 0){// first or 2nd iteration
num =0;
itertn++;
}else{
return;//after 2nd stop
}
}
console.log(num);//the work
num++;
setTimeout(nxt, 1000, num, itertn);//next after a second
}
But there are other ways to do this
I think that you're trying to do sort of a counter
Try something like this:
for(let i=1;i<=3;i++){
setTimeout(()=>{ console.log(i)}, i*1000 )
}
Let me know if this solve your problem
Try nested loop
for(let i=0;i<2;i++){
for(let j=0;j<3;j++){
setTimeout(()=>{console.log(j)}, 1000)
}
}
One solution is create promises, and with map and promise.all, you can group each loop of promises and execute them sequentially.
(async () => {
const second = (i) => new Promise(resolve => setTimeout(() => {
console.log(i)
resolve(i)
}, 1000))
const loop = () => Array(3).fill(0).map(async(item, index) => {
return await second(index)
})
var startTime = performance.now()
await Promise.all(loop())
var endTime = performance.now()
console.log(endTime - startTime)
await Promise.all(loop())
endTime = performance.now()
console.log(endTime - startTime)
})()
I have a javascript loop that executes a couple of functions. The first function loads an iframe and the second function clicks an element on that iframe. I want the loop not to run the second function until the iframe is finished loading. But I am not sure how to achieve this.
So far I have done this but doesn't seem to do the job
Loop :
action.steps.forEach(step => {
window[step.functionName](step.functionParameter);
});
First function
function goToUrl(url) {
let iframeDocument = document.querySelector('iframe');
iframeDocument.src = url;
let iframeLoaded;
iframeDocument.onload = () => {
iframeLoaded = true
}
async function checkLoad() {
if (iframeLoaded) {
alert("page loaded");
return true;
} else {
await sleep(500);
checkLoad();
}
}
checkLoad();
}
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
second function
function clickOnElement(elementSelector) {
var element = iframeDocument.querySelector(elementSelector);
element.click();
}
The first function is an asynchronous operation, which can be modified to return a Promise that resolves when the frame is loaded. This also means you don't need to recursively checkLoad.
function goToUrl(url) {
const iframeDocument = document.querySelector('iframe');
iframeDocument.src = url;
return new Promise((resolve, reject) => {
// calls `resolve()` when loaded
iframeDocument.onload = resolve;
});
}
The second function needs to wait for the first function to be resolved.
To generalise this pattern, you can modify your loop as an asynchronous function,
which awaits for the result of a step if that step's function returns a Promise (e.g. your goToUrl function:
async function yourLoop() {
// each step could be synchronous or asynchronous
for (const step of actions.step) {
const result = window[step.functionName](step.functionParameter);
if (result instanceof Promise) {
// if step is asynchronous operation, wait for it to complete
await result;
}
}
}
/////// usage ////////
yourLoop().then(() => {
/* all steps completed */
}).catch(() => {
/* some step(s) failed */
});
Whenever a timer or event handler is needed, I usually prefer not to do a traditional loop nor mess with promises/awaits.
Instead what I would do is something similar to this. Basically waiting in onload to run the callback function then moving on to the next function in the loop.
let max = action.steps.length;
let cur = 0;
function loadIframe(url) {
let iframeDocument = document.querySelector('iframe');
iframeDocument.src = url;
let iframeLoaded;
iframeDocument.onload = () => {
let step = action.steps[cur];
window[step.functionName](step.functionParameter);
if(cur < max){
cur++;
loadIframe(step.url)
}
}
}
loadIframe(action.steps[cur].url);
I am unsure how the usage of returning a new Promise vs using a Promise.resolve() and want to make sure my understanding of these are correct.
Given these 3 functions below:
function testFunc() {
return Promise.resolve().then(() => {
//anything asynchronous in here has now become synchronous and
//execution of result of the function happens after this??
let i = 0;
while (i < 10000) {
console.log(i);
i++;
}
});
}
------
function testFunc2() {
return new Promise((resolve, reject) => {
//anything asynchronous in here is still asynchronous but the
//`resolve()` is then synchronous??
let i = 0;
while (i < 10000) {
if (i === 999) { resolve('I am a test func') };
i++;
}
})
}
------
//synchronous function
function logMe() {
let i = 0;
while (i < 10000) {
console.log("INSIDE LOG ME);
i++;
}
}
The way I understand it is that testFunc() immediately resolves the Promise and anything within there becomes synchronous. So if you were to execute:
testFunc();
logMe();
testFunc() would fully execute before logMe() was reached and executed.
For the testFunc2(), if it were executed in this order:
testFunc2();
logMe();
I understand it as the logic inside, in this case the while loop, would still execute synchronously and delay the execution of the following function, logMe(), but the resolve would be treated asynchronously.
It’s not that easy. You would have to use test Func.then(logMe()). Another option would be to run both functions in an asynchronous function:
async function run() {
await testFunc();
logMe();
}
The await is pretty simple - it runs the function and waits for it to finish, then runs the next lines. async is just there so that await can work (await does not work outside of async functions).
I prefer async/await, but many more prefer .then. It’s just what you want to use, both are very similar.
If I understand correctly what you're trying to achieve, you want to asynchronously execute operation inside the loop.
function testFunc2() {
const promises = []
for(let i = 0; i<1000; i++){
promises.push(new Promise((resolve) => {
console.log(i);
resolve()
}));
}
return Promise.all(promises);
}
function logMe() {
let i = 0;
while (i < 5) {
console.log("INSIDE LOG ME");
i++;
}
}
(async() => {
await testFunc2()
logMe();
})();
I want to get data from db for several times. How to set a loop to execute getData() function in interval like 200ms. And if one of them success, rest of them won't be triggered. It's asynchronous method, and different from question here: Asynchronous Process inside a javascript for loop
for(var i = 0; i < 3; i++){
setTimeout(getData,200);}
This will end up with the output time interval is very close instead of 200ms, since they are asynchronous. Three "setTimeout" are triggered in a short time. like 0.001s 0.002s 0.003s, the output time is 0.201, 0.202, 2.203.
getData() returns a promise. But it can be normal function as long as it works.
You can do this by waiting for the setTimeout to finish before executing the next setTimeout
I don't believe this is possible with callbacks, as is the case with setTimeout, so you should transform it into a promise-based call
const promiseSetTimeout = timeout => new Promise(r => setTimeout(r, timeout));
const waitManyTimes = async () => {
for(let i = 0; i < 3; i++) {
await promiseSetTimeout(200);
// do something here, like getDB
console.log(i);
}
}
waitManyTimes();
Don't use a loop. Have the function call setTimeout() to run itself again if it fails.
var i = 0;
function callGetDB() {
getDB().then(db => {
// use DB
}).catch(() => {
if (i++ < 3) {
setTimeout(callGetDB, 200);
}
});
}
I'm assuming the asynchronous function getDB() returns a promise.
Using async/await:
let sleep = ms => new Promise(r => setTimeout(r, ms));
let main = async() => {
for(var i = 0; i < 3; i++) {
let db = getDB();
if (db)
return db;
await sleep(200);
}
};