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.
Related
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);
function page() {
return new Promise((resolve, reject) => {
setTimeout(function() {
fo = $("#root>div>div>main>div>div:nth-child(3)>div>div:nth-child(2)>div>div>div:nth-child(2)>div:nth-child(2)>div>div:nth-child(2)>div>div:nth-child(2)>div>ul>li:nth-last-child(1)")
fo.click()
console.log('翻页')
resolve();
}, 200)
})
}
function l() {
for (let i = 0, p = Promise.resolve(); i < 10; i++) {
p = p.then(() => {
return new Promise(resolve =>
setTimeout(function() {
console.log('wo')
$('#root>div>div>main>div>div:nth-child(3)>div>div:nth-child(2)>div>div>div:nth-child(2)>div:nth-child(2)>div>div:nth-child(2)>div>div:nth-child(1)>div:nth-child(1)>table>tbody>tr>td:nth-child(7)>div>div:nth-child(2)>a>span').eq(i).click()
resolve();
}, 200)
)
})
.then(() => {
return new Promise(resolve =>
setTimeout(function() {
console.log('wocao')
$('body>div:nth-last-child(1)>div>div>div:nth-child(3)>div:nth-child(2)>button>span').click()
resolve();
}, 200)
)
}
)
}
}
Promise.resolve().then(l).then(page)
Why is the program working not in order of the promises? How can I solve this problem? I've tried many times but they still execute together but not in order. Could somebody teach me? Very thanks.
Your function l is a unit function (i.e. returns nothing) and is therefore not awaitable as it does not return a Promise (either explicitly, or via being marked as async).
You should:
function l() {
let p = Promise.resolve() // this needs to exist out of the loop scope
for (let i = 0; i < 10; i++) {
p = p.then( /* ... */ )
}
return p; // <-- so it can be returned here
}
You need to resolve inside the timeout
new Promise(function(resolve, reject) {
// the function is executed automatically when the promise is constructed
// after 1 second signal that the job is done with the result "done"
setTimeout(() => {console.log("done");resolve()}, 1000);
}).then(function (res2, rej2) {
setTimeout(() => {console.log("done2")}, 1000);
})
but in your case you do not define parameters to the promise, hence they are default.
I have a Subject, which used to be fire async chain.
Its sometimes looks like working correctly, sometimes looks like the subscribe doesn't wait for the resolve.
behavior.subscribe(async (result)=> {
await fnc1(result);
});
fnc1(in) {
return new Promise(async (resolve,reject) => {
for(let w of in.split(',')) {
await fnc2(w);
}
//if for finished, go to next subscribed value
resolve();
});
}
fnc2(w) {
return new Promise((resolve,reject) => {
let i = 0;
setInterval( () => {
i = i + 1;
if(i == 10) {
//go next value in for loop
resolve();
}
},100);
});
}
I want to execute some code after a certain delay in a loop. And once all the iterations are done, I want to do some other tasks. These depend on the results obtained from task1. For this I have written the following code snippet using generator, async/await and promise:
function* iter() {
for (var i = 0; i < 10; i++) yield i
}
async function start() {
var myIter = iter();
var p = await cb1(myIter);
console.log('after await');
p.then((value) => {
console.log('-----------here-------------');
});
}
start();
function cb1(myIter) {
console.log("Started : " + new Date());
var obj;
return new Promise((resolve, reject) => {
setTimeout(function(){
if(myIter.next().done === true) {
console.log("End : " + new Date());
resolve('done');
}else {
console.log("---in else---");
cb1(myIter);
}
}, 3000);
});
}
The issue is, the console in p.then() never gets printed. This means that the promise never gets resolved and the program terminates. The iterations execute as expected, but the promise never resolves. What could be wrong here? I am using recursion to trigger iterator.next() and want to resolve the promise only on the last iteration, that is when done=true.
Scratching my head since long on this issue. Help is appreciated. Following is the output of this program.
A couple problems: the line
var p = await cb1(myIter);
results in p being assigned the value of the result of calling cb1. p is not a Promise unless the constructed Promise resolves to a Promise as well, which is unusual. Because await essentially pauses the execution of the script until the promise resolves, you don't need .then - you just need to add the console.log below in the start function. Once you chain the promises together properly, p will resolve to a string of 'done', which of course isn't a Promise.
But there's another problem: your promises returned by cb1 never resolve, except at the very end, where yuo're calling resolve. In the else, you're not ever calling resolve, so those promises remains unresolved forever. To fix this, change
} else {
console.log("---in else---");
cb1(myIter);
}
to
} else {
console.log("---in else---");
cb1(myIter).then(resolve);
}
so that the current iteration's Promise resolves once the next iteration's Promise resolves.
function* iter() {
for (var i = 0; i < 3; i++) yield i
}
async function start() {
var myIter = iter();
var p = await cb1(myIter);
console.log('after await');
console.log('-----------here-------------');
}
start();
function cb1(myIter) {
console.log("Started : " + new Date());
var obj;
return new Promise((resolve, reject) => {
setTimeout(function() {
if (myIter.next().done === true) {
console.log("End : " + new Date());
resolve('done');
} else {
console.log("---in else---");
cb1(myIter).then(resolve);
}
}, 1000);
});
}
There are a couple of issues:
The result of await will never be a promise; the purpose of await is to wait for the promise to resolve and give you the resolution value. So p in your code isn't a promise, and won't have a then method. But you're not getting an error about that because of #2.
Each call to cb1 creates a new promise, with a new resolve function. Your last setTimeout callback is resolving the last promise, but nothing ever resolves the first one, so you never get past that var p = await cb1(myIter); line.
You probably want an inner function for the timer callback, and then have the promise returned by the cb1 call resolve.
Something along these lines:
function* iter() {
for (var i = 0; i < 10; i++) {
yield i;
}
}
async function start() {
var myIter = iter();
var p = await cb1(myIter);
console.log("p = ", p);
}
start();
function cb1(myIter) {
console.log("Started : " + new Date());
return new Promise((resolve, reject) => {
iteration();
function iteration() {
setTimeout(function() {
if (myIter.next().done) { // `=== done` is pointless here
console.log("End : " + new Date());
resolve('done');
} else {
console.log("---in else---");
iteration();
}
}, 3000);
}
});
}
Need 1 more function then it works:
function* iter() {
for (var i = 0; i < 10; i++) yield i
}
async function start() {
var myIter = iter();
var p = await cb1(myIter);
console.log('after await');
console.log("here is p:" + p.done + "," + p.value);
}
start();
function repeat(myIter,resolver,previous){
var temp;
if((temp = myIter.next()).done === true) {
console.log("End : " + new Date());
resolver(previous);
}else {
console.log("---in else---");
setTimeout(function(){repeat(myIter,resolver,temp)},3000);
}
}
function cb1(myIter) {
console.log("Started : " + new Date());
var obj;
return new Promise((resolve, reject) => {
repeat(myIter,resolve);
});
}
Also fixed the p, await gets it out, and you needed to grab the previous value
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);
});
}