Better way to wait for a function until next step javascript - javascript

I am writing a browser-based javascript to scrape the input value by user and provide feedback. And in the page, you have to click on different areas for the desired element to show. I am trying to see if there is a way I can get javascript to wait until the element returns before I start going to the next one. Right now I just use setTimeout, but I have to loop all the elements I want to get, so nasty nested functions. trying to see if there is a more elegant solution. I used to write VBA, so I am not so familiar with async web API. still learning
function a(){
document.getElementById('1').click()
setTimeout(function(){document.getElementById('1a');addmessage;b;}),5000}
}
function b(){
document.getElementById('2').click()
setTimeout(function(){document.getElementById('2a');addmessage;c;}),5000}
}
function c(){
document.getElementById('3').click()
setTimeout(function(){document.getElementById('3a');addmessage;d;}),5000}
}
function addmessage(){
//this is the function I used to add element do some operation and add to dialog box
}
etc..
what I imagined is like below
function addmessage(element_to_click,element_to_collect){
// click element_to_click
// wait 5s
// collect innertext from element_to_collect
// do some operation, generate a message
//add message to dialog box
}
so I can do something like
addmessage(1,1a)
addmessage(2,2a) <--- this won't execute until the first is complete.

If I'm understanding the question correctly, you can start with a promise wrapper around setTimeout:
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
Then addMessage could be:
async function addMessage(element_to_click, element_to_collect) {
element_to_click.click();
await delay(5000);
return element_to_collect.textContent;
}
You didn't say what you wanted to do with the text, so I've just had it return it.
Then you can either write a loop or a series of calls:
async function run() {
const a = await addMessage(
document.getElementById("1"),
document.getElementById("???"),
);
const b = await addMessage(
document.getElementById("2"),
document.getElementById("???"),
);
const c = await addMessage(
document.getElementById("2"),
document.getElementById("???"),
);
// ...do something with `a`, `b`, and `c`...
}
When calling run, don't forget to handle promise rejection (in case something goes wrong).
You've said that after the click, "...the page needs a few seconds to load..." Hardcoded timeouts are fragile and often break. Instead of a timeout, try to hook into something that will change, perhaps via a MutationObserver on the element you're "collecting" from.

You can make your code wait for a timeout to complete by wrapping it in a Promise and using the async await pattern:
// Return a new Promise immediately, that completes at the end of the timeout
function addmessage(element_to_click, element_to_collect) {
return new Promise((resolve, reject) => {
// do whatever
// resolve the Promise after 5s
setTimeout(resolve, 5000)
// you can also resolve the Promise on any other event, like user input
// (or use `reject` when an error occurs)
// you can pass a return value to `resolve` like resolve(input.value)
})
}
// `async` functions also return a Promise immediately
async function addMessages() {
// `await` will block until Promise completes and
// returns the resolved value to be used synchronously within the async func
const in1 = await addmessage('1', '1a')
const in2 = await addmessage('2', '2a')
const in3 = await addmessage('3', '3a')
}
addMessages()

Related

Repeatedly call a function that returns a promise until one of them contains a specific response

Hello I need to call a REST function with an ID, that returns a promise in React.js. This function will at some point contain a certain value in its response when called . Until another service has processed an initial request this value will be null.
This is what I have done so far:
while(myVariable){
myfunction(myID).then( (response) => {
if(response['value'] != null
myVariable = false;
}
});
}
The problem with this code is that the while loop is called as fast as possible and thus completely utilises the computer. So I need a function that allows me to poll for a result by an ID until the response of one of the function calls contains a valid response.
I have tried the following method but without success, because I don't have a fixed number of runs:
Wait promise inside for loop
Thanks in regards.
As you state, the problem is that the while loop runs eagerly, not waiting for each promise to resolve.
One way to solve that is to use recursion. Recursion gives you more control over when exactly you want to 'loop' next:
let getValue = () => {
myFunction(myID).then(response => {
if (response['value'] === null) {
setTimeout(getValue);
} else {
// here you know the other service has processed the initial request
}
});
};
First I wrapped the whole thing in a function called getValue. Note that this function is only called again after the promise resolves. (The call to setTimeout is a trick to use recursion without consuming the stack.) If this still runs too quickly, pass an additional parameter of 100 or so to the setTimeout invocation.
Alternatively, you may be able to use async/await similarly to the link you shared. I'm no expert on async/await but it should work the same with while loops as with for loops, judging by this and this.
You can use the async function with await.
I also use a delay function to delay each call to the myfunction().
While you get a response, you can break the while loop.
const delay = ms => new Promise((resolve, reject) => setTimeout(resolve, ms));
async function main() {
const myID = 1;
let response;
while (true) {
response = await myfunction(myID);
if (response["value"] != null) {
break;
}
await delay(5000);
}
//do Something once you get the response here below:
}
main();

How to wait for JavaScript to finish in playwright

I am testing UI with Playwright and JavaScript. My code find an input element that can sometimes be a drop down, sometimes a text and sometimes a date. To handle this I enter value with 2 steps. First I fill the text and then click on tab key to invoke the JavaScript that formats the value in the element.
await page.fill("#myID", inputText);
await page.keyboard.press('Tab'); // this line trigger the JS
// continue to the next element
The problem, it is not waiting for JavaScript to finish. How can I wait for JS to finish before the code continue.
You could wait for some reaction to your click (some page change, or HTTP request made), for example:
wait for certain element state change https://playwright.dev/docs/api/class-locator#locator-wait-for
wait for a new element to appear on the page (like a dialog open) https://playwright.dev/docs/api/class-page#page-wait-for-selector
wait until a request fired https://playwright.dev/docs/api/class-page#page-wait-for-request
wait until a response received https://playwright.dev/docs/api/class-page#page-wait-for-response
or wait for an event https://playwright.dev/docs/api/class-page#page-wait-for-event
Using the page.waitFor... functions
There are a slew of functions that playwright offers for when certain conditions are met that start with page.waitFor (e.g. page.waitForFunction). Probably page.waitForFunction is the most versatile, because you can pass a custom function that waits for a specific condition to be met.
Alternatively, use a timeout
I think you can use setTimeout with page.evaluate inside the page context to wait a bit for other JavaScript to run:
await page.evaluate(() => {
// if this doesn't work, you can try to increase 0 to a higher number (i.e. 100)
return new Promise((resolve) => setTimeout(resolve, 0));
});
This might be equivalent to page.waitForTimeout(0), but I'm not sure. Note that they recommend not to use page.waitForTimeout in production.
You might find this useful, (https://repl.it/join/bkgmwcjv-vladimirlisove1). https://developer.mozilla.org/enUS/docs/Web/JavaScript/Reference/Statements/async_function.
function resolveAfter2Seconds() {
return new Promise(resolve => {
setTimeout(() => {
resolve('resolved');
}, 2000);
});
}
async function asyncCall() {
console.log('calling');
const result = await resolveAfter2Seconds();
console.log(result);
// expected output: "resolved"
}
asyncCall();
alternatively, found an example from freecodecamp, which appears to be simpler:
const promise = new Promise((resolve, reject) => {
setTimeout(function(){
resolve(); // Task is completed
}, 3000);
return resolve;
});
promise.then(res => {
console.log('resolved');
}).catch(err => {
console.log(err)
});

Async / await vs then which is the best for performance?

I have a simple code in JavaScript that execute a request in an API and return the response, simple. But in this case I will have thousands of requests. So, which one of the code options will perform better, and why. Also which one is recommended as good pratices these days?
First options is using the .then to resolve the promises and the seccond one is using async / await.
In my tests the two options had very similar results without significant differences, but I'm not sure in scale.
// Using then
doSomething(payload) {
const url = 'https://link-here/consultas';
return this.axios.get(url, {
params: {
token: payload.token,
chave: payload.chave,
},
}).then(resp => resp.data);
}
// Using Async / await
async doSomething(payload) {
const url = 'https://link-here/consultas';
const resp = await this.axios.get(url, {
params: {
token: payload.token,
chave: payload.chave,
},
});
return resp.data;
}
Any explanation will be of great value.
From a performance point of view, await is just an internal version of .then() (doing basically the same thing). The reason to choose one over the other doesn't really have to do with performance, but has to do with desired coding style or coding convenience. Certainly, the interpreter has a few more opportunities to optimize things internally with await, but its unlikely that should be how you decide which to use. If all else was equal, I would choose await for the reason cited above. But, I'd first choose which made the code simpler to write and understand and maintain and test.
Used properly, await can often save you a bunch of lines of code making your code simpler to read, test and maintain. That's why it was invented.
There's no meaningful difference between the two versions of your code. Both achieve the same result when the axios call is successful or has an error.
Where await could make more of a convenience difference is if you had multiple successive asynchronous calls that needed to be serialized. Then, rather than bracketing them each inside a .then() handler to chain them properly, you could just use await and have simpler looking code.
A common mistake with both await and .then() is to forget proper error handling. If your error handling desire in this function is to just return the rejected promise, then both of your versions do that identically. But, if you have multiple async calls in a row and you want to do anything more complex than just returning the first rejection, then the error handling techniques for await and .then()/.catch() are quite different and which seems simpler will depend upon the situation.
There should be some corrections in this thread. await and .then are going to give very different results, and should be used for different reasons.
await will WAIT for something, and then continue to the next line. It's also the simpler of the two because it behaves mechanically more like synchronous behavior. You do step #1, wait, and then continue.
console.log("Runs first.");
await SomeFunction();
console.log("Runs last.");
.then splits off from the original call and starts operating within its own scope, and will update at a time the original scope cannot predict. If we can put semantics aside for a moment, it's "more asynchronous," because it leaves the old scope and branches off into a new one.
console.log("Runs first.");
SomeFunction().then((value) => {console.log("Runs last (probably). Didn't use await on SomeFunction().")})
console.log("Runs second (probably).");
As more explanation to #user280209 answer let's consider the following function which returns promise and compare its execution with .then() and async await.
function call(timeout) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`This call took ${timeout} seconds`);
resolve(true);
}, timeout * 1000);
});
}
With .then()
(async () => {
call(5).then((r) => {
console.log(r);
});
await call(2); //This will print result first
await call(1);
})();
When running the above call the logs will be
This call took 2 seconds
This call took 1 seconds
This call took 5 seconds
true
As we can see .then() didn't pause the execution of its below line until it completes.
With async/wait
(async () => {
await call(5); //This will print result first
await call(2);
await call(1);
})();
When run the above function logs will be
This call took 5 seconds
This call took 2 seconds
This call took 1 seconds
So I think if your promise's result won't be used in the following lines, .then() may be better.
For those saying await blocks the code until the async call returns you are missing the point. "await" is syntactic sugar for a promise.then(). It is effectively wrapping the rest of your function in the then block of a promise it is creating for you. There is no real "blocking" or "waiting".
run();
async function run() {
console.log('running');
makePromises();
console.log('exiting right away!');
}
async function makePromises() {
console.log('make promise 1');
const myPromise = promiseMe(1)
.then(msg => {
console.log(`What i want to run after the promise is resolved ${msg}`)
})
console.log('make promise 2')
const msg = await promiseMe(2);
console.log(`What i want to run after the promise is resolved via await ${msg}`)
}
function promiseMe(num: number): Promise<string> {
return new Promise((resolve, reject) => {
console.log(`promise`)
resolve(`hello promise ${num}`);
})
}
The await line in makePromises does not block anything and the output is:
running
make promise 1
promise
make promise 2
promise
exiting right away!
What i want to run after the promise is resolved hello promise 1
What i want to run after the promise is resolved via await hello promise 2
Actually.
Await/Async can perform more efficiently as Promise.then() loses the scope in which it was called after execution, you are attaching a callback to the callback stack.
What it causes is: The system now has to store a reference to where the .then() was called. In case of error it has to correctly point to where the error happens, otherwise, without the scope (as the system resumed execution after called the Promise, waiting to comeback to the .then() later) it isn't able to point to where the error happened.
Async/Await you suspend the exection of the method where it is being called thus preserving reference.
If we just consider performance(time taken) then it actually depends on whether your operations are serial or parallel. If your tasks are serial then there will be no difference between await and .then. But if your tasks are parallel then .then will take less time. Consider the following example
let time = Date.now();
// first task takes 1.5 secs
async function firstTask () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
},1500)
})
}
// second task takes 2 secs
async function secondTask () {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2);
},2000)
})
}
// using await
async function call(){
const d1 = await firstTask();
const d2 = await secondTask();
console.log(Date.now()-time, d1+d2)
}
call()
// using .then
async function call2(){
let d1=null,d2=null;
firstTask().then(data => {
d1=data;
if(d2){
console.log(Date.now()-time, d1+d2);
}
})
secondTask().then(data => {
d2=data;
if(d1){
console.log(Date.now()-time, d1+d2);
}
})
}
call2()
Here are the two tasks, first takes 1.5 secs and second takes 2 secs. Call function uses await where as call2 function uses .then . The output is as follows
From call2 2012 3
From call 3506 3
I hope it helps.
As far as I understand .then() and await are not the same thing. An async function won't proceed with the next command until the promise is resolved/rejected since it's basically an implementation of generators. On the contrast, in the case of .then(), the execution of the function will proceed with the next command and the resolve/reject callback will be executed "when there's time" aka when the current event loop (not entirely sure about that part) will be completed.
tldr; on a single promise await and .then() behave similarly but when one promise needs another one to be resolved first then the two of them behave entirely different
Many answer have been provided to this question already. However, to point out key information in the answers above and from my understanding, note below point:
only use await when not handling error return
if no crucial need for error handling use await instead
use .then .catch if returned error message or data is crucial for debugging / or proper error handling instead of try catch for await
Choose any prefer method from code sample below
const getData = (params = {name: 'john', email: 'ex#gmail.com'}) => {
return axios.post(url, params);
}
// anywhere you want to get the return data
// using await
const setData = async () => {
const data = await getData();
}
// to handle error with await
const setData = async () => {
try {
const data = await getData();
}
catch(err) {
console.log(err.message);
}
}
// using .then .catch
const setData = () => {
var data;
getData().then((res) => {
data = res.data; console.log(data)
}).catch((err) => {
console.log(err.message);
});
}

Resolve await when message arrives

I have some websocket code in JS. I have a message-handling loop like this:
socket.addEventListener('message', function (event) {
payload = JSON.parse(event.data)
method = payload.method
// Dispatch messages
if (method == 'cmd1') {
handle_cmd1(payload); // trigger event/semaphore here to wait up waiter
}
else if (method == 'cmd2') { ... }
});
And elsewhere, I have a button callback like this:
$('#my-button').change(function() {
handle_button();
});
async function handle_button() {
send_msg('msg1', 'hi');
// wait for server to reply with cmd1
cmd1_data = await something(); // what?
alert(`cmd1 data: $(cmd1_data)`);
}
The idea is that the button sends 'msg1' and the server is supposed to reply with 'cmd1' and some info. I want to wait for that reply and then do some more stuff.
So my question is how to interlock these? In C++ I'd use a semaphore. I'd rather not spin-loop; is there something in Javascript/JQuery I can use to trigger and then wait for a user-defined event like this? I'm sort of new to JS, and very new to JS async/await.
EDIT: I've made a simple jsfiddle to show what I'm after.
http://jsfiddle.net/xpvt214o/484700/
Now that I understand how promises in Javascript work, here's a working example of a promise that can get woken up from anywhere by calling a function:
wakeup = null;
// returns a promise that will be resolved by calling wakeup()
// (could be a list of these or whatever, this is just a simple demo)
function wakeable() {
return new Promise( (resolve) => {
wakeup = () => { resolve(true); }
});
}
// demo of waiting and getting woken up:
async function handle_event() {
while (true) {
console.log("waiting...")
await wakeable(); // returns to event loop here
console.log("handle event woke up!");
}
}
handle_event(); // start in "background"
wakeup(); // wake it up
setTimeout(_ => { wakeup(); }, 500); // wake it up again after a delay
What's happening here is that when you call wakeable(), it returns a promise. That promise is constructed with an anonymous function (the one taking resolve as arg); the promise constructor synchronously calls that function, passing it the promise's resolve method. In our case, the function sets wakeup to another anonymous function that calls the original resolve; it's a closure so it has access to that resolve function even when it's called later. Then it returns the new promise.
In this demo, we then await on that promise; that puts the pending promise on a queue, saves the current function state, and returns, like a generator calling yield.
A promise is resolved when its resolve function is called; in this case calling wakeup() calls the promise's internal resolve() method, which triggers any .then methods on the next tick of the Javascript event loop (using the promise queue mentioned above). Here we use await, but .then(...) would work the same way'
So there's no magic; I/O and timeout promises work the same way. They keep a private registry of functions to call when the I/O event or timeout happens, and those functions call the promise's resolve() which triggers the .then() or satisfies the await.
By the way, unlike async in python, leaving a pending promise "open" when the process exits is perfectly fine in Javascript, and in fact this demo does that. It exits when there's no more code to run; the fact that the while loop is still "awaiting" doesn't keep the process running, because it's really just some closures stored in a queue. The event loop is empty, so the process exits (assuming it's in node.js -- in a browser it just goes back to waiting for events).
something() should be a method that returns a promise() or should be another method that is also notated with async.
function something(){
return new Promise(resolve,reject) {
//... call your database
// then
resolve(theResult)
// or
reject(theError)
}
}
The async and await for the most part are really just wrappers around promises. The await returns when the promise calls resolve, and throws an exception when the promise calls reject.
Your async function can return another promise; If it returns another value, it gets turned into a resolved promise with that value.

What does it mean for promises to be immutable and their guaranteed value?

I'm trying to understand the differences between es6 promises and regular callbacks but don't get the examples below. Can someone show what it would look like to do the below with callbacks?
// an immediately resolved promise
var p2 = Promise.resolve("foo");
// can get it after the fact, unlike events
p2.then((res) => console.log(res));
var p = new Promise(function(resolve, reject) {
setTimeout(() => resolve(4), 2000);
});
// handler can't change promise, just value
p.then((res) => {
res += 2;
console.log(res);
});
// still gets 4
p.then((res) => console.log(res));
A promise is a one-way latch. Once it is resolved with a value or rejected with a reason, its state and value/reason can never change. So, no matter how many times you do .then() on the same promise, you will always get the same result. That's what "immutable" means.
I'm not sure what you mean by a guaranteed value. There is no guarantee that a promise will ever resolve. It might reject (and thus not have a value) or it might never resolve or reject if the operation just never completes.
An example of the type of operation promises are designed for is an asynchronous operations such as an Ajax call or reading some bytes from a file. The operation is asynchronous (normal execution of the interpreter continues after the operation was started) and the operation has a specific start and end to it. In most case, the operation may complete successfully in which case it can have a value or it may end with an error in which case it has an error. Both value and error can be objects so they can have many properties if the result is more than a simple value.
An Ajax call, for example has a specific start and end. It can't end more than once so it is a perfect match for promises. You get a promise that signifies the eventual result of the ajax operation. You then register both a fulfill handler and a reject handler and one or the other will be called when the operation has completed.
Plain callbacks are just callbacks and they can be given a different value every time they are called and they can be called more than once.
If you want to get notified once and only once when some operation completes and the operation has a specific begin and end, use a promise.
If you want to get notified more than once, use a plain callback or an event listener or an observer or some other mechanism that can be trigger more than once.
As a simple example, setTimeout() works very well with a promise.
function delay(t) {
return new Promise((resolve, reject) => {
resolve();
}, t);
}
// delay 100ms before starting the operation
delay(100).then(run);
Or, a little more involved operation using the Bluebird Promise library to cycle through a list of URLs, download the content, parse the content, look in the content for some specific URLs and then collect them all (otherwise known as scraping):
const Promise = require('bluebird');
const request = Promise.promisifyAll(require('request'), {multiArgs: true});
const cheerio = require('cheerio');
function getAsync() {
return request.getAsync.apply(request, arguments).then(argArray => {
// return only the response html
if (argArray[0].statusCode !== 200) {
throw new Error("response statusCode = " + argArray[0].statusCode);
}
return argArray[1];
});
}
const urls = [....];
Promise.mapSeries(urls, url => {
return getAsync({url: url, gzip: true}).then(html => {
let $ = cheerio.load(html);
let resources = $("#external_resources_list li a.filename");
resources.each(index, link) => {
let href = $(link).attr("href");
console.log(href);
results.push(href);
});
}).catch(err => {
// log error, but keep going
console.log(url, err);
});
}).then(() => {
// got all results here
console.log(results);
});
And, setInterval() does not work at all with a promise because it wants to notify you repeatedly everytime the time interval passes and that will simply not work with promises. Stick with a callback for setInterval().
To make the comparison with a standard callback system, let's create a class that can produce such notifier objects. It will have an interface much like Promise has, but which implements a simple callback system:
class Notifier {
constructor(executor) {
// The object will maintain a list of callbacks
this.callbacks = [];
// The executor is executed now and will allow the client
// to determine the logic of this notifier object:
// ...when the callbacks need to be called and with which value:
executor( (res) => {
// The client can call this resolve function to indicate
// the value. So now the callbacks need to be called with it:
this.callbacks.forEach(callback => callback(res));
});
}
addListener(callback) {
// This method resembles the `then` of promises: it allows
// a client to pass a callback function, that should be called
// when the value becomes available (i.e. when the event triggers).
this.callbacks.push(callback);
}
};
So, like with Promise, you can pass to the constructor of this class a function to do some work and indicate the value at the appropriate time. You can also attach listeners to it, which will be called at the moment the value becomes available.
This last phrase highlights an important fact: if the value becomes available, but you did not attach a listener (callback) yet, you'll miss the notification, even if you attach the listener after the facts.
Here is the callback-based code which you could compare with the code you quoted from the article:
class Notifier {
constructor(executor) {
// The object will maintain a list of callbacks
this.callbacks = [];
// The executor is executed now and will allow the client
// to determine the logic of this notifier object:
// ...when the callbacks need to be called and with which value:
executor( (res) => {
// The client can call this resolve function to indicate
// the value. So now the callbacks need to be called with it:
this.callbacks.forEach(callback => callback(res));
});
}
addListener(callback) {
// This method resembles the `then` of promises: it allows
// a client to pass a callback function, that should be called
// when the value becomes available (i.e. when the event triggers).
this.callbacks.push(callback);
}
};
// a notifier that immediately notifies the result
f2 = new Notifier( (resolve) => resolve("foo") );
// but since no-one was listening, no callback is called.
// canNOT get it after the fact, unlike promises
f2.addListener((res) => console.log(res));
// ... nothing gets called or printed: we are too late.
//
var f = new Notifier(function(resolve) {
setTimeout(() => resolve({ data: 4}), 2000);
});
// handler CAN change the outcome
f.addListener((res) => {
res.data += 2;
console.log(res.data);
});
// ... now also this one gets 6!
f.addListener((res) => console.log(res.data));
when the promise variable is resolved, the value resolved when it is recalled returns.
to use more than one, you must call it as follows.
var p = new Promise(function(resolve, reject) {
setTimeout(() => resolve(4), 2000);
});
p.then((res) => {
res += 2;
console.log(res);
return res
})
.then((res) => console.log(res));

Categories