converting synchronous code to asynchronous code - javascript

I am learning to develop chrome extensions(and at the same time learning javascript as well). I am experimenting with an extension that runs on youtube webpage. The thing is when I scroll, sometimes the webpage kinda feels a little jittery.
So, I started looking for possible reasons. I came across a few good videos including "what the heck is event loop by Philip Roberts" in which he talked about event loop and other related stuff. This made me wonder if it's because of this very reason the webpage feels jittery.
The code in the content script is synchronous and I don't have much experience with asynchronous coding in javascript. Concepts like promises, async/await are new to me.
Here is the code in the content script at a very high level.
chrome.storage.sync.get(); // getting some data here
let activateButton = document.createElement("div");
activateButton.id = "activate-ext";
activateButton.addEventListener("click", getData);
there is a call to chrome storage api
some code to render a button(there is a click event listener attached to this button which renders another UI.)
then I added this button to the dom
Now, most of the stuff is happening in the getData callback.
When a user clicks on that button. the extension:
retrieve data from the webpage and does some regex matching and then the string values that I want is stored in an array a.
then when I have the array a, I am using a map on it to create another array b
then another operation on all the elements of b to get another array c.
and now finally when I have the array c, I call another function that generates the final UI(using values from the array c)
This is how getData looks like:
function getdata(){
const regex = /some_regex/g;
let match = regex.exec(data);
while (match) {
a.push(match[index]);
match = regex.exec(description)
}
b = createAnotherArray(a) // this function returns array b
c = anotherOperation(b)
generateUI(c) // in this function I am rendering the UI
}
Now, all this code is synchronous(apart from the call to chrome storage API and some callbacks) and I believe some of these operations(like regex matching) take time which blocks the event loop.
So what would be the best way to restructure this whole code using promises(async/await) so it does not block the browser from rendering/updating UI.
I also saw some loops with async/await and I too have loops in my code too. So, do I need to do that too?

JavaScript is single-threaded. Event loop included - simply, it's just a queue for tasks put into it. If your JS code is actually causing performance issues doing some stuff asynchronously won't help, since then that code will block the event loop anyway.
Only available option is Web workers.

Related

Is creating a function to do a small repeated task an anti-pattern?

I am trying to learn best practices for programming and want to keep my code as clean as possible, but also maintainable. For example, I am running a program that waits for each element to be created.
a.waitForElementToAppear(50000);
b.waitForElementToAppear(50000);
c.waitForElementToAppear(50000);
Now, I added a function so I can easily change the 50000 without having to manually edit each one:
function waitForElement(element) {
element.waitForElementToAppear(5000);
}
and have changed the above code to:
waitForElement(a);
waitForElement(b);
waitForElement(c);
Is it an anti-pattern to create a new function to call for a relatively small task? Is there a better approach?
Is it an anti-pattern to create a new function to call for a relatively small task?
No, it is absolutely fine! Although you're right that your function is so small that it hardly gains anything.
Is there a better approach?
To achieve your goal of easily changing the shared argument value, you could alternatively (not necessarily "better") also put it in a variable:
const time = 50000;
a.waitForElementToAppear(time);
b.waitForElementToAppear(time);
c.waitForElementToAppear(time);
Last but not least, that code still is a bit repetitive. Another option would be a loop (although those three items are just about the threshold for a loop being sensible):
for (const element of [a, b, c]) {
element.waitForElementToAppear(5000);
}

JS Loop - how do I loop through X items then wait for response to finish the loop

I want to loop through X items but I want only to loop through only two of those items then wait for a response back from a function when its done then start another loop an so on. So, that only two loops only run at any given time until all items haven finished. What is he best efficient way to accomplish this?
The good news is the single-threaded nature of javascript works to your advantage here. The bad news is that if your work requires any form of asynchronous input, you'll have to A) write thread-blocking/locking code, or B) get creative with recursion and setTimeout.
The simpler solution (A) goes something like this. Let's assume you need to prompt the user every two loops to continue the operation.
for( var i=0, n=0; i < 10000; i++, n++ ){
if ( n == 2 ) {
/* Example of thread blocking operation
"confirm" function blocks the thread, so
the loop will stop executing until the user
clicks "ok"
*/
if ( !confirm("Keep looping?") ){
break;
};
// Reset n so that it fires again in 2 iterations
n = 0;
}
}
Of course, if your work doesn't require asynchronous input from the user or an ajax call, all the better.
Fortunately, a more graceful solution is just around the corner. The latest javascript spec (ES6) uses something called generators to accomplish exactly what you're trying to do (you can think of them as an async-await). Unfortunately, support for generators varies and is generally considered a more advanced topic, so your mileage may vary.
Use promises.
Let's say you have a list of items to process. Create a function that takes an item off of the list, wraps it in a promise, initiates it, and then on resolution of the promise, invokes itself. Be sure to include logic to cleanly handle reaching the end of the list!
Call the function twice. You now have two execution pipelines running. Be aware that unless there's async processing going on, they're not really executing in parallel - remember we're single-threaded.
If the list is being added to WHILE items are being run, you're going to need some additional logic around restarting if something is added after it's been emptied.

What is the order of execution between python code and javascript code in html

I am using bottle framework to dynamically list a link for editing a task, rows is a collection of tuple with multiple (id, task, status) logically the code below should give the first link
http://localhost:8217/edit/1
second link
http://localhost:8217/edit/2
so on and forth
but all the link are
http://localhost:8217/edit/5
therefore I suspect this has to do with the order the code is executed, any hint would be much appreciated
<p>The open items are as follows:</p>
<table border="1">
%for row in rows:
<tr>
%for col in row:
<td>{{col}}</td>
%end
<script language="javascript">
var number = "http://localhost:8217/edit/";
var key = {{row[0]}};
console.log(key);
</script>
<td>
<a href="" onClick="location.href=number +key;return false";>Editing</a>
</td>
</tr>
%end
</table>
Python code runs on the server; Javascript code runs on the client.
Therefore, Javascript code can only run after the Python code finishes.
However, this has nothing to do with your problem.
HTML tags do not create local variable scopes; all of your rows share the same key variable.
By the time you click the link, key will always have the last value (since all of the JS has already executed).
You should build an href attribute in server-side code instead.
The "problem" is in your javascript. It's not really a problem though, it's just the way the javascript event loop works. The short answer, is that when your browser is reading the rendered template, it loads the onClick events into the event queue, but doesn't actually call them. Only after it's done processing the page, does it start going through the event queue and reading them one-by-one. But at this point, all the <script> tags in your page have processed, and the current value of key will be the last value it had from the loop, presumably 5 in the examples you gave. As the browser handles the events from the event queue, it will just assign the current value of key to the onClick handle, which is why they all get the value 5.
One possible fix is to use a closure to take a "snapshot" of the current value of key as the script tags are read. To create the closure I will use a self-invoking function, but there are other ways to do this:
var key = (function() {
return {{row[0]}};
)();
This is actually a very common problem, so you might want to look around for a more thorough explanation. A good starting point is the subsection "The infamous loop problem" here: Javascript Scope and Closures

How to measure User Interaction time of UI, menu made by Javascript?

I want to measure UI interaction time like following.
Time between Menu selected and showing result.
Time between User typing & showing letters in UI
I think many people suggest me to measure the time between function call & result, but I want to know another way to get the time though UI changes.
Is it possible to check the time for UI changes ?
The tool what I'm developing is made by Javascript and run on Browser.
Here is my answer:
"Javascript is single threaded".
So,what does this means:
Javascript runs code sequentially.It is not possible to run two
different pieces of Javascript code at the same time because it does
not support multithreading.
Execution of javascript code is line by line.So execution of multiple lines at same time is not possible(it will be very small though)
A simple solution:
check_my_time_first() ;//these will be functions returning the epoch time
check_my_time_after();
You just need console.log,flag variables and epoch time.
Firstly,the difference will be ultra small.
var milliseconds_before = (new Date).getTime();//use this code when user clicks
var milliseconds_after = (new Date).getTime();//use this when result appears.
Make use of flag variables to know when some execution has been completed.
Another example:
var execution_over=false;
function doSomething(){
console.log("start_time"+milliseconds_before)
//some code to be executed;
execution_over=true
if(execution_over==true){
console.log("time="+milliseconds_after)
}
}
Difference:
var diff=milliseconds_before - milliseconds_after;
Place this code smartly and you will see the difference in time.
The important thing is to understand the fundamentals.I hope my answer helped.

JavaScript pausing execution of function to wait for user input

I'm trying to make a sort of game using the HTML5 canvas, JavaScript and XML.
The idea is that you can make a quiz by putting questions and answers in an XML file.
I wrote a main loop that loops through all the questions, poses them and checks the correctness of the answer.
For now I'm simply using alerts and dialog boxes to answer the questions
The problem is that my main loop is one big interconnected whole that walks through the entire game from beginning to end, and instead of having alert boxes posing questions and dialog boxes to answer, immediately after one another, I want some user interaction.
The answers to the questions appear on boxes at the bottom of the screen, and the user gets to control a crane to pick the right answer.
Here's the snippet of code from the main loop I'm stuck on:
answer = loadQuestion(i);
if (answer == "correct") {
// answered correctly, update scoreArray and load next question
scoreArray[currentQuestion] = "correct";
// show 'next'-button and wait for press to continue
} else {
// answered incorrectly again, update scoreArray and load next question
scoreArray[currentQuestion] = "error";
// show 'next'-button and wait for press to continue
}
As you can see I'm calling loadQuestion, which instantly loads the question, shows the possible answers, and for now immediately throws a dialog box where you can type the answer.
This answer is returned and validated.
I have already programmed the controls of the crane, the user can already pick up a box with it.
But because I'm calling loadQuestion and expecting it to return a value, this doesn't work.
How do I make my main loop "pause" until an answer has been given by the player using the crane, and then proceed?
I already tried making the answer a global variable, and just having an empty while answer == "" to keep the function busy doing nothing until answer gets a value, but this just freezes the script.
I also messed around with intervals in an attempt to monitor the status of the answer variable, and clear the interval and return the value when this happens, but that simply returns false since the function completes without immediately returning a value.
How do I make my main loop "pause" until an answer has been given by the player using the crane, and then proceed?
By breaking it up. The only "yield" in JavaScript on browsers is to let your function end and then arrange to get called back later (via setTimeout, setInterval, an ajax callback, etc.). In your case, I'd tend to think the trigger to call you back should be the user's action answering the previous question, e.g., a click handler on the answer boxes or some such (rather than setTimeout and such, which are automated).
For instance, this code:
function loopArray(ar) {
var index;
for (index = 0; index < ar.length; ++index) {
doSomething(ar[index]);
}
}
...can be recast like this:
function loopArrayAsync(ar, callback) {
var index;
index = 0;
loop();
function loop() {
if (index < ar.length) {
doSomething(ar[index++]);
setTimeout(loop, 0);
}
else {
callback();
}
}
}
The second version yields control back to the browser on every loop iteration. It's also important to note that the second version returns before the loops have been completed, whereas the first version waits until all loops are done, which is why the second version has the callback function it calls when it's done looping.
Code calling the first one might look like this:
var a = ["one", "two", "three"];
loopArray(a);
// Code that expects the loop to be complete:
doTheNextThing();
doYetAnotherThing();
...whereas using the async version looks like this:
var a = ["one", "two", "three"];
loopArrayAsync(a, function() {
// Code that expects the loop to be complete:
doTheNextThing();
doYetAnotherThing();
});
Doing this, you'll probably find you use closures (loop, above, is a closure), and so this article may be useful: Closures are not complicated
There is no sync. paused input/output tools in browser JS except of alert, confirm and prompt. Since you don't want to use those - try the following pattern:
loadQuestion(i, function(answer) {
if (answer == "correct") {
// answered correctly, update scoreArray and load next question
scoreArray[currentQuestion] = "correct";
// show 'next'-button and wait for press to continue
} else {
// answered incorrectly again, update scoreArray and load next question
scoreArray[currentQuestion] = "error";
// show 'next'-button and wait for press to continue
}
resumeGameExecution();
});
I.e. when user answers it - the callback function gets executed and you know that you're good to go.
From what I understand you have a questionnaire application, where you ask a series of questions and then ask the user to drop his answer using some drag and drop control ( a crane).
I am going to go an a tangent and say the design seems to be wrong. Javascript is event based and having one main thread looping around for user interaction is going to reduce the usability. I will not use any way of stalling the thread ( aka in java). Javascript is not written for such an usecase. Your application will be perceived as non responsive by some browsers and by almost all performance analyzers (like Yslow).
So I would provide each question with a div identified by a class which internally is a sequence (question1..2). Initially only one question will be enabled or visible. Once user answers the question, I will enable the enabled question. ( on the appropriate event, in this case probably the drop event of drag and drop). Since it is sequential, I will just have to check if the user has answered question1, then I will enable question2 appropriately. The whole flow should be event driven. The event here being the user answering the question.

Categories

Resources