How to use await with setTimeout and setInterval? - javascript

I have a function createSession for getting users with returned session object. createSession either returns users or throws string exception. My code is as follow, if session is successfull (which means I have users) continue code.
However I do not know when users are available because other resources also asks for it. So, I want a modify my code that it will try getting users in a timeout, lest say try it every 1 second until timeout 30 seconds reached.
async function callTest() {
const capabilities = {
type: {
chrome: 2
}
}
var session = await working.createSession(capabilities)
if(!session) return
callTest()
// remaining code goes here
}
I though to use setTimout in a promise but the problem is I can not use await inside a normal function. Can someone help me fixing my code ?
var checkAuth = function(capabilities) {
return new Promise(function(resolve) {
var id = setInterval(function() {
var session = await working.createSession(capabilities)
if (session) {
clearInterval(id);
resolve(id);
}
});
}, 10);
});
}

You can try to use it like this and it should work well
var checkAuth = function(capabilities) {
return new Promise(function(resolve) {
setInterval(async function() {
var session = await working.createSession(capabilities)
if (session) {
clearInterval(id);
resolve(id);
}
}, 10);
});
}
You should add async keyword to function definition in setInterval, because await can only work in async functions.
Update: also move ,10 parameter from Promise definition to setInterval one

Related

How to catch an error on a async callback function on outer try/catch block

Ok,
So I am using the puppeteer framework and I have an async function that interact with a webpage.
This function clicks and selects and elements of a webpage while it waiting for the traffic of the page to be idle.
This function works most of the time, but sometimes it stalls.
I want to be able to set a timeout so that if the function is taking longer than a certain amount of time, it throws an error and I can run it again.
So far I cannot seem to get this to work because I cannot get the callback function I pass to setTimeOut() to 'interact' with the outer function.
My code looks like this:
const scrap_webtite = async page => {
/* scrap the site */
try{ // catch all
// set timeout
let timed_out_ID = setTimeout(()=> throw "timeOut", 1000);
// run the async
let el = await sometimes_stalls_function(page);
// if function ran finished correcly
clearTimeout(timed_out_ID);
// save el
save_el(el);
}
}catch(e){
console.error("Something went wrong!", e);
// this makes the function run again
// here is where I want to ideally catch the timeout error
return false
}
}
I have also tried wrapping the setTimeOut function in an Promise as per this post and the using the .then().catch() callbacks to try to catch the error to no avail.
Apologies if this is a stupid question, thank for you help.
The problem you're running into is essentially that the error thrown in setTimeout() is not related to your function flow, and thus can't be caught there. You can essentially think of the timer's callback function as a "detached" function: the variables from the parent scope will still be available, but you can't return a value to the parent directly etc.
To work around this problem you have a few options, Promise.race() is one possible solution. The idea is to first make an async version of a timeout:
const rejectAfter = (timeout) => {
return new Promise((resolve, reject) => {
setTimeout(() => reject(), timeout);
});
};
Then extract your business logic out into a separate async function a-la:
const doTheThing = async () => {
// TODO: Implement
};
And finally in your scraping function, use Promise.race() to use the result from whichever of the two finishes first:
const scrape = async (page) => {
try {
const el = await Promise.race([
rejectAfter(1000),
doTheThing()
]);
} catch(error) {
// TODO: Handle error
}
}
try turning everything in the try block into a promise
const scrap_webtite = async page => {
/* scrap the site */
try{ // catch all
return await new Promise(async(r,j)=>{
// set timeout
let timed_out_ID = setTimeout(()=>j("timeOut"),1000);
// run the async
let el = await sometimes_stalls_function(page);
// if function ran finished correcly
clearTimeout(timed_out_ID);
// save el
r(save_el(el));
})
}catch(e){
console.error("Something went wrong!", e);
// this makes the function run again
// here is where I want to ideally catch the timeout error
return false
}
}

Wait for request to finish with setInterval()

Consider the code below. This code sets an interval of 5 seconds before making another request to load the new values into part of a document.
The problem is that when a request takes a long time to resolve (>5 seconds) the requests will pile up. How can we make sure the new request will only be made when the request has finished?
In my internet search trying to answer this question, I have not come across this specific situation including setInterval. I came across a couple of suggestions using async or promises, but I have yet to discover how those could be implemented in this situation. Please forgive my inexperience in the topic.
window.onload = () => {
setInterval(refresh, 5000);
}
function refresh() {
let myVariable = document.getElementById("myId").value;
// reload part of the page for the new values
$("#partial-id").load("partial.html", {
myparameter: myVariable ,
});
}
Call the function again in the .load() callback function.
function refresh() {
let myVariable = $("#myId").val();
$("#partial-id").load("partial.html", {
myparameter: myvariable
}, function() {
setTimeout(refresh, 5000);
});
}
This will repeat the function 5 seconds after the previous one completed.
If the task performed gets wrapped into a promise, one can await it in an infinite loop:
function refresh() {
let myVariable = document.getElementById("myId").value;
return new Promise(res => {
// reload part of the page for the new values
$("#partial-id").load("partial.html", {
myparameter: myVariable ,
}, res);
});
}
(async function () {
while(true) {
await refresh();
await new Promise(res => setTimeout(res, 5000));
}
})();

Nodejs running function on background

I’ve nodes program which I need to run two function in the beginning of the program
And later on access the function results, currently with await each function at a time this works,
However in order to save a time and not waiting to GetService and GetProcess as I need the data later on in the project
It takes about 4 seconds to get this data and I want to run it on the background as I don’t need the results immediately,
How I can do it in node js, If I run promise.all It would wait until the getService and getProcess and then go to rest of the program.
an example
function main() {
//I want to run this both function in background to save time
let service = await GetServices();
this.process = await GetProcess();
…..//Here additional code is running
//let say that after 30 second this code is called
Let users = GetUser(service);
Let users = GetAdress(this.process);
}
im actually running yeoman generator
https://yeoman.io/authoring/
https://yeoman.io/authoring/user-interactions.html
export default class myGenerator extends Generator {
//here I want run those function in background to save time as the prompt to the user takes some time (lets say user have many questions...)
async initializing() {
let service = await GetServices();
this.process = await GetProcess();
}
async prompting() {
const answers = await this.prompt([
{
type: "input",
name: "name",
message: "Your project name",
default: this.appname // Default to current folder name
},
{
type: "confirm",
name: "list",
choises: this.process //here I need to data from the function running in background
}
]);
}
Let's assume that getServices() may take 3 seconds and getProcess() may take 4 seconds, so if you run these both functions at the same time you will be returned in total 4 seconds with the return values from both promises.
You can execute the code while this process is running in the background there will be a callback when the promises resolved, your late functions will be called at this stage.
Check the below simple example;
let service;
let process;
function main() {
// Both functions will execute in background
Promise.all([getServices(), getProcess()]).then((val) => {
service = val[0];
process = val[1];
console.log(service, process);
// Aafter completed this code will be called
// let users = GetUser(service);
// let users = GetAdress(process);
console.log('I am called after all promises completed.')
});
// Current example.
// let service = await GetServices();
// this.process = await GetProcess();
/* Code blocks.. */
console.log('Code will execute without delay...')
}
function getServices() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("service is returned")
}, 3000);
});
}
function getProcess() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("process is returned")
}, 4000);
});
}
main();
You can start the asynchronous operation but not await it yet:
function suppressUnhandledRejections(p) {
p.catch(() => {});
return p;
}
async function main() {
// We have to suppress unhandled rejections on these promises. If they become
// rejected before we await them later, we'd get a warning otherwise.
const servicePromise = suppressUnhandledRejections(GetServices());
this.processPromise = suppressUnhandledRejections(GetProcess());
// Do other stuff
const service = await servicePromise;
const process = await this.processPromise;
}
Also consider using Promise.all() which returns a promise for the completion of all promises passed to it.
async function main() {
const [ services, process, somethingElse ] = await Promise.all([
GetServices(),
GetProcess(),
SomeOtherAsyncOperation(),
]);
// Use the results.
}
To do what who you need, you have to understand the event loop.
Nodejs is designed to work in a single thread unlike languages like go, however nodejs handle proccess on different threads. so you can use nextTick () to add a new event to the main thread and it will be executed at the end of the whole block.
function main() {
//I want to run this both function in background to save time
let service = await GetServices();
this.process = await GetProcess();
…..//Here additional code is running
//Let say that after 30 second this code is called
Let users = GetUser(service);
Let users = GetAdr(this.process);
}
function someFunction(){
// do something...
}
main();
process.nextTick(someFunction());// happens after all main () processes are terminated...

Nested multilayered async/await doesn't seem to wait

I have a piece of code simplified version of which looks like this:
let dataStorage1; //declare global vars for easier access later on
let dataStorage2;
let stopLight = true; //this variable is used to 'mark' an iteration as successful (= true) or
//failed (= false) and in need of a retry before continuing to the next
//iteration
let delay = 2000; //the standard time for a delay between api calls
async function tryFetch() {
try {
dataStorage1 = await api.fetch('data_type_1'); //fetch needed data trough api, which
//fills the global variable with an
//object
dataStorage2 = await api.fetch('data_type_2'); //do the same
stopLight = true; //change the value of stopLight to true, thus marking this iteration
//as successful
} catch (err) {
console.log(err);
stopLight = false;
}
}
async function fetchData() {
stopLight = true; //change the stopLight to default before execution
await tryFetch(); //fetch data and assign it to variables
//this section is needed for retrial of fetching after a 2s delay if the first attempt was
//unsuccessful, which is repeated until it's either successful or critical error occurred
while (stopLight == false) {
setTimeout(async () => await tryFetch(), delay);
}
}
(async function main() {
await fetchData(); //finally call the function
setTimeout(main, delay); //repeat the main function after 2s
})();
As you can see, self-executing, pseudo-recursive main() calls for await fetchData(), then fetchData() calls for await tryFetch() and finally tryFetch() calls for await api.fetch('~'), as it's defined in the api.
However, once I started the script and paused it after a couple of iterations, I noticed that both dataStorage1 and dataStorage2 remain undefined. If I go through the code step by step in debugger, what happens is that the execution starts at the beginning of fetchData(), moves to the await tryFetch(); line, skips it, and then goes onto the next iteration.
For the reference, if I call dataStorage1/2 = await api.fetch(`~`); in the body of main() directly without any nesting, it works perfectly (unless error occurs, since they are not handled properly).
So, my question is what have I missed?
Indeed, if in an async function you call setTimeout you cannot expect it to perform an await on anything that relates to the callback passed to setTimeout. The call to setTimeout returns immediately, and your while loop is effectively a synchronous loop. It is a so called "busy loop" -- blocking your GUI as it potentially will loop for thousands of times.
As a rule of thumb, use setTimeout only once: to define a delay function, and then never again.
Also avoid using a global variable like stopLight: this is bad practice. Let the async function return a promise that resolves when this is supposed to be true, and rejects when not.
// Utility function: the only place to use setTimeout
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function tryFetch() {
try {
let dataStorage1 = await api.fetch('data_type_1');
let dataStorage2 = await api.fetch('data_type_2');
return { dataStorage1, dataStorage2 }; // use the resolution value to pass results
} catch (err) {
console.log(err);
// retry
throw err; // cascade the error!
}
}
async function fetchData() {
while (true) {
try {
return await tryFetch(); // fetch data and return it
} catch (err) {} // repeat loop
}
}
(async function main() {
let intervalTime = 2000; //the standard time for a delay between api calls
while (true) { // for ever
let { dataStorage1, dataStorage2 } = await fetchData();
// ... any other logic that uses dataStorage1, dataStorage2
// should continue here...
await delay(intervalTime); //repeat the main function after 2s
}
})();
I think the problem is in this line: setTimeout(async () => await tryFetch(), delay);. The await statement inside the callback makes the promise returned by that callback wait, not the whole function. So async () => await tryFetch() is a function that returns a promise, but nothing waits for that promise to complete.
Try replacing that code with something line
await new Promise((resolve) => setTimeout(resolve, delay));
await tryFetch();

Await a promise multiple times in parallel

I have some server API methods I call during startup of my application at different places, which can't be refactored in terms of where they are called, let's say getSettings() and getSchedule(). Both methods are based on a login(): Promise<Account> method as settings as well as the schedule are user-based.
Now I have solved the beginning of getSettings() and getSchedule() like the following:
class UserFunctions {
private fetch_login: Promise<AccountItem> = undefined;
async getSchedule(): Promise<any> {
var account = getAccount();
if (!account) {
// No account loaded
let isLogginIn = this.fetch_login;
if (!this.fetch_login) {
// Not logging in from any parallel method
this.fetch_login = login();
}
account = await this.fetch_login;
this.fetch_login = undefined;
}
// Now get schedule...
}
}
The idea behind is that the login() function is only called once no matter how often it could be called. That's why I keep a reference on the Promise to await it multiple times. This works, but I noticed that sometimes when login() is done getSettings() gets earlier the okay to continue work and getSchedule() stays a few seconds until it continues executing. Sometimes it's the other way around and sometimes both methods return in the same time.
Here I have a print of the output:
06-05 16:46:08.126 27376 27397 I ReactNativeJS: Logging in back
06-05 16:46:08.690 27376 27397 I ReactNativeJS: Logged in back
06-05 16:46:08.696 27376 27397 I ReactNativeJS: Schedule downloaded
06-05 16:46:09.274 27376 27397 I ReactNativeJS: Logged in back
Do you have any idea how the code can be improved that once login() is done, both methods continue working?
I think you've got the right idea, but the logic needs a little tweaking:
var account = getAccount();
if (!account) {
// No account loaded
if (!this.fetch_login) {
// Not logging in from any parallel method
this.fetch_login = login();
}
account = await this.fetch_login;
}
Basically the idea is that you'll set fetch_login to have the value of the promise the first time it gets called. After that, you can await that same promise as many times as you need to.
Two things:
You're calling fetch_login. You should just be referring to it:
account = await this.fetch_login; // <== No ()
Remove this line:
this.fetch_login = undefined;
E.g.:
async getSchedule(): Promise<any> {
var account = getAccount();
if (!account) {
// No account loaded
if (!this.fetch_login) {
// Not logging in from any parallel method
this.fetch_login = login();
}
account = await this.fetch_login;
}
// Now get schedule...
}
You keep the promise resulting from the login attempt, so you can await it each time. (Nothing about awaiting a promise prevents your doing so again.)
It seems like this should be handled at the level of getAccount and login, though, rather than within UserFunctions. getAccount and login appear to be global (within your app or module or whatever). I would probably have them manage this, with getAccount returning a promise:
var accountPromise = null;
function getAccount() {
if (!accountPromise) {
accountPromise = login();
}
return accountPromise;
}
then
async getSchedule(): Promise<any> {
var account = await getAccount();
// Now get schedule...
}
Example:
var accountPromise = null;
function getAccount() {
if (!accountPromise) {
accountPromise = login();
}
return accountPromise;
}
function login() {
return new Promise(resolve => {
console.log("Logging in...");
setTimeout(() => {
console.log("Logged in");
resolve({}); // {} = the account
}, 800);
});
}
class UserFunctions {
/*private*/ fetch_login/*: Promise<AccountItem>*/ = undefined;
async getSchedule()/*: Promise<any>*/ {
var account = await getAccount();
// Now get schedule...
console.log("Got account, getting schedule");
}
}
// The first time it's used
const u1 = new UserFunctions();
u1.getSchedule(); // Logs in and gets schedule
// The second time it's used
setTimeout(() => {
const u2 = new UserFunctions();
u2.getSchedule(); // Gets schedule (already logged in)
}, 2000);

Categories

Resources