I am developing a web interface for Arduino, using Python. For automatic updates and display, I use JSON. I have a very interesting problem.
The following code sends the command to a python function, if a command exists. Then, whether a command was sent to the function or not, the function checks for updates from the Arduino by calling another function.
Here is what I can't find any explanation to: in the first and only condition of the update() function, if I remove the line that says alert('hey'); the python function is not called anymore. But if I do write alert('hey'); after the JSON request, it works fine, the function is called and the arduino gets the message.
Anyone has an idea why?
function update(command=0) {
// if a command is passed, send it
if (command!=0) {
$.getJSON('/action?command='+command);
alert('hey'); // if I remove this, the action function is not called. Why?
}
// read from the read function, no matter what
$.getJSON('/read', {}, function(data) {
if (data.state != 'failure' && data.content != '') {
$('.notice').text(data.content);
$('.notice').hide().fadeIn('slow');
setTimeout(function () { $('.notice').fadeOut(1000); }, 1500);
}
setTimeout(update, 5000); // next update in 5 secs
});
}
update(); // for the first call on page load
Are you checking for the results of the first command with the second? If so, I suspect the alert('hey') is pausing execution long enough for the first to finish. Can you try making your read a callback function of the first getJSON?
function update(command=0) {
if (command!=0) {
$.getJSON('/action?command='+command, function() {
read();
});
} else {
read();
}
}
function read() {
$.getJSON('/read', {}, function(data) {
if (data.state != 'failure' && data.content != '') {
$('.notice').text(data.content);
$('.notice').hide().fadeIn('slow');
setTimeout(function () { $('.notice').fadeOut(1000); }, 1500);
}
setTimeout(update, 5000); // next update in 5 secs
});
}
update(); // for the first call on page load
Here's a fiddle
Related
TL;DR:
loadingSpinner div toggled on before expensive code, toggled off after
both showLoading() and hideLoading() call log() which writes a message to console.log() and an element's innerHTML
the loadingSpinner and log message in the DOM do not show up before the expensive code is done but the console.log() messages show up when they should
I have a reference to a div stored in loadingSpinner which is just a box that sits above all the other content that should indicate that the site is doing some work. I use these functions to toggle visibility of said div (.hidden is just display: none; in my CSS)
function hideLoading() {
log('hiding')
loadingSpinner.style.display = 'none'
//setTimeout(function (){loadingSpinner.style.display = 'none'}, 10)
//window.getComputedStyle(loadingSpinner) // <-- TRIED FORCING REDRAW
//if (!loadingSpinner.classList.contains('hidden')) {
//loadingSpinner.classList.add('hidden')
//}
}
function showLoading(text) {
log('Showing')
loadingSpinner.innerHTML = text
loadingSpinner.style.display = 'block'
//setTimeout(function (){loadingSpinner.style.display = 'block'}, 10)
//window.getComputedStyle(loadingSpinner)
//if (loadingSpinner.classList.contains('hidden')) {
//loadingSpinner.classList.remove('hidden')
//}
}
function log(s) {
console.log(s)
logDisplay.innerText = s
}
The commented out code are different things I've tried already. The show and hide functions themselves work fine. I can tell that the hide and show functions are called at the right time because of the calls to log().
I have a few instances where the site does some expensive/long running tasks on the client of which nothing should be asynchronous, as far as I can tell (not sure about Array.prototype.forEach()). The Problem is that the loadingSpinner only shows up after the expensive task has completed and then hideLoading() hides it immediately. I did confirm this by adding a setTimeout() to hideLoading().
function updateDOM() {
showLoading('Updating DOM...') // <--- SHOW
log('Updating DOM') // <--- OTHER LOG MESSAGE
codeContainer.innerHTML = '' // <--- start of long running task
codes.forEach(code => {
if (code.base64 === '') {
backgroundQr.set({value: code.data})
code.base64 = backgroundQr.toDataURL()
}
addCodeElement(codeContainer, code)
});
if (codes.length === 0) {
editingId = -1
} // <--- end of long running task
hideLoading() // <--- HIDE
}
Console Log order is correct:
Showing
Updating DOM
hiding
But neither the text that log() writes to the logDisplay-Element nor the loadingSpinner itself show up when they should so I assume it is a rendering issue?
The issue is consistent in multiple places, not just the updateDOM() function.
As expensive code is being executed synchronously, the browser is too busy running it to find any time to render things to the DOM. One approach you can take is to execute expensive code asynchronously using promises and the setTimeout function to delay the expensive execution or send it to the end of the execution queue.
I've created the code snippet below that shows the approach, you'll need:
Spinner handling functions
Expensive executor function
Asynchronous code runner
Your main script that puts them all together
The snippet below contains two examples that you can toggle between, one performs a success execution, by running main();, the other a failure execution, by running main(true);.
function showSpinner() {
document.querySelector('#spinner').style.display = 'block';
}
function hideSpinner() {
document.querySelector('#spinner').style.display = 'none';
}
function executeSuccess() { // runs something expensive that succeeds
return 'data';
}
function executeFailure() { // runs something expensive that fails
throw 'issue';
}
function promiseToRunAsync(executor, ...params) {
return new Promise((resolve, reject) => {
setTimeout(() => {
try { resolve(executor(...params)); }
catch (error) { reject(error); }
}, 1000); // arbitrary time that you can set to anything including 0
});
}
function main(failure = false) {
showSpinner(); // show spinner
promiseToRunAsync(failure ? executeFailure : executeSuccess) // execute anync
.then((results) => {
console.log('yay', results);
document.querySelector('#content').innerHTML = results;
hideSpinner(); // hide spinner in case of success
})
.catch((error) => {
console.log('oops', error);
hideSpinner(); // hide spinner in case of failure
});
// ATTN code here will run before the spinner is hidden
}
main(); // executes the success scenario
// main(true); // executes the failure scenario
#spinner {
display: none;
}
<div id="spinner">Spinning...</div>
<div id="content"></div>
NOTE: In the example here, I am adding a 1 second delay to the execution, just to illustrate what's happening, but you'll probably need to set your own wait time or no wait time at all.
I think your problem is that the code is asynchronous.
In the second you start your forEach loop, the code continues all the way to hideLoading while the forEach loop is still executing, therefore you will never see the loader because you call showLoading and hideLoading right after each other.
Try changing your code like this:
function updateDOM() {
showLoading('Updating DOM...') // <--- SHOW
log('Updating DOM') // <--- OTHER LOG MESSAGE
codeContainer.innerHTML = '' // <--- start of long running task
for (const code of codes) {
if (code.base64 === '') {
backgroundQr.set({value: code.data})
code.base64 = backgroundQr.toDataURL()
}
addCodeElement(codeContainer, code)
}
if (codes.length === 0) {
editingId = -1
} // <--- end of long running task
hideLoading() // <--- HIDE
}
I am running a HTTP Request to a file and depending on the response whether it be "200" or another response a success or error function is ran. This request takes place every second.
The problem I am facing is when I get lots of error responses they all run together and the last one doesn't stop e.g. End the interval to start a new one.
The red light begins to flash way too fast. Can anyone help me out. My code is below and I have been playing with it for a few hours now but can't seem to get to the bottom of it.
var requestResponses = {
greenLight: $('.cp_trafficLight_Light--greenDimmed'),
redLight: $('.cp_trafficLight_Light--redDimmed'),
greenBright: 'cp_trafficLight_Light--greenBright',
redBright: 'cp_trafficLight_Light--redBright',
init: function (url) {
setInterval(function () {
requestResponses.getResponse(url);
}, 1000);
},
successResponse: function () {
var redBright = requestResponses.redBright,
greenBright = requestResponses.greenBright;
requestResponses.errorCode = false;
requestResponses.redLight.removeClass(redBright);
requestResponses.greenLight.addClass(greenBright);
},
errorResponse: function () {
requestResponses.runOnInterval();
},
runOnInterval: function () {
// clearInterval(runInterval);
var redBright = requestResponses.redBright,
greenBright = requestResponses.greenBright,
redLight = requestResponses.redLight;
requestResponses.greenLight.removeClass(greenBright);
var runInterval = setInterval(function () {
if (requestResponses.errorCode === true) {
redLight.toggleClass(redBright);
}
}, 400);
},
getResponse: function (serverURL) {
$.ajax(serverURL, {
success: function () {
requestResponses.errorCode = false;
requestResponses.successResponse();
},
error: function () {
requestResponses.errorCode = true;
requestResponses.errorResponse();
},
});
},
errorCode: false
}
requestResponses.init('/status');
Appreciate the help.
Javascript is an event driven language. Do not loop inifinitely to check things periodically. There are places to do so but most of the time either calling a delay function (setTimeout) repeatedly when needed or using a callback would be better method.
Using setInterval with request, think what happens if requests start taking longer than your interval.
In your case, you have two loops created with setInterval. First one is the request which will run every 1 sec. Instead of using setInterval, you can modify your code to run setTimeout only after a request finishes and do other tasks just before re-running the next request :
function runRequest(...) {
$.ajax(serverURL, {
...
complete: function () {
setTimeout(runRequest, 1000);
}
...
});
}
function lightsOnOff() {
var redBright = requestResponses.redBright,
greenBright = requestResponses.greenBright,
redLight = requestResponses.redLight;
requestResponses.greenLight.removeClass(greenBright);
if (requestResponses.errorCode === true) {
redLight.toggleClass(redBright);
}
}
setInterval(lightsOnOff, 400);
The setInterval() method repeats itself over and over, not just one time. Your error response handler is then invoking the routine that creates another setInterval(), and so on. Until you have so many processes running that you get the flashing red light issue.
The solution is to only invoke the logic where the setInterval() call is made once. Or, even better, use setTimeout() to call the routine. It is run one-time and likely better for your use.
I am adding some qunit test cases for a module. Few of the test cases have sync processes which I am using the standard stop() and start() as per docs.
My questions is, isn't the fact that the extra 1 second from setTimeout(function () { start();}, 1000); is added to the runtime of the test run, making the results in accurate?
I am a little not satisfied that +1000ms is added to the runtime as outside of the testsuite, inside the real app that uses that module that process completes without the 1000ms added here to carry out the test.
So when I pass this interface to less technical tester I have to explain in the title of the test to subtract that 1000 from that test before adding them up or whatever to calculate overall speed etc. [I basically want a way to have that extra timeout removed from the results automatically]
Module code below:
define("tests/admin.connections.tests", ["mods/admin.connections", "datacontext"], function (connections, datacontext) {
module("ADMIN PAGE CONNECTION LIST MODULE", {
setup: function () {
//ok(true, "once extra assert per test for Search Modules");
}
});
test('Module is available?', function () {
equal(_.isUndefined(connections), false, "connections js module exists");
equal(_.isObject(connections), true, "connections js module is valid object");
});
test('HTML and CSS loading correctly? [Subtract 1 second from time to get the real time lapsed]', function () {
function testHtml(html) {
var d = document.createElement('htmlTestDiv');
d.innerHTML = html;
return d.innerHTML.replace(/\s+/g, ' ');;
}
stop();
$.get('http://media.concepglobal.com/cbaddons/templates/connections.html', function (data) {
equal(testHtml(connections.html), data.replace(/\s+/g, ' '), 'Html of the module was correctly loaded');
});
$.get('http://media.concepglobal.com/cbaddons/styles/connections.css', function (data) {
equal(testHtml(connections.css), data.replace(/\s+/g, ' '), 'CSS of the module was correctly loaded');
});
setTimeout(function () { start();}, 1000);
});
test('getConnectionsByUserId Async Method [Subtract 1 second from time to get the real time lapsed]', function () {
function getConnectionsByUserId(successCallback) {
amplify.request("getConnectionsByUserId", { uid: '0' }, function (data) {
connections.userConnectionsCallback(data);
successCallback();
});
}
stop();
getConnectionsByUserId(function () {
var connectionsReturnedData = connections.connectionListViewModel.connections();
expect(2);
ok(_.isArray(connectionsReturnedData), 'Valid array has been returned for connections: ' + connectionsReturnedData);
equal(connectionsReturnedData[0].type(), "sitecore", 'First returned object has a type property of "' + connectionsReturnedData[0].type() + '" and we expected it to be "sitecore"');
});
setTimeout(function () { start(); }, 1000);
});
});
QUnit saves the currently running test in QUnit.config.current, this allows you to change the test during it's execution.
What you probably want is to reset the timer of the test after the timeout.
I created a little example to show what I mean (see on jsFiddle):
asyncTest("Long running test with 2s timeout", function () {
expect(1);
ok(true);
setTimeout(function () {
QUnit.config.current.started = +new Date();
start();
}, 2000);
});
Like that the timer is reset once the timeout is over. This results in more accurate runtime in terms of what is executed. Now only the total time shows how much time was actually used to run all tests.
Not sure if my question is subjective/objective but as a JavaScript newbie i'm encountering this problem quite a lot. So here I go.
I'm used to write C#, so my JavaScript structure looks like C#. And just that, that gives problems I think ;-)
Let's give a simple example where I met my problem again today:
MyLibrary.fn.InitAddEntityForm = function () {
$('a#btnAddEntity').click(function () {
//post data and receive object with guid and isPersisted boolean
var persistedObject = MyLibrary.fn.CheckAndSendAddEntityForm("name", "avatarurl.png");
console.log("test");
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject.gdEntityId);
} else {
alert("Oops, something went wrong. Please call 911");
}
});
};
//////*****/////
//SOME FUNCTION THAT SENDS MY FORM AND RETURNS AN OBJECT WITH TRUE VALUE AND POSTED ENTITY ID
/////*****//////
MyLibrary.fn.CheckAndSendAddForm = function (txtName, ImageUrl) {
var postUrl = "/admin/add";
var persistedObject = new Object();
$.post(
postUrl,
{ Name: txtName, ImageUrl: txtImageUrl},
function (data) {
if (data.Status == 200) {
console.log("Post status:" + data.Message);
persistedObject.isPersisted = true;
persistedObject.gdEntityId = data.Data;
} else if (data.Status == 500) {
console.log("Failed to post entitiy");
} else {
console.log("Fault with Javascript");
}
}, "json"
);
return persistedObject;
};
Okay, thats it. Everything looks okay right? Browser says no.
I tried to debug it using firebug, looping over my code line by line, and that way the browser does what I want: Execute a new function to show the next panel in my wizard.
After placing a lot of Console.logs() in my code I figured out that this must be something about timing in JavaScript. In C# the code executes line by line, but apparently JavaScript doesn't.
By placing that Console.log("test") I noticed that "test" appeared in my console before "Post status: Success!".
So here's my question, how should I write my JavaScript code so I have control over the way the browser executes my code?
Should I really replace the code below to the end of my CheckAndSendAddEntityForm()?
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject.gdEntityId);
} else {
alert("fout");
}
Is this how I have to write JavaScript: One big domino effect or am I just doing something wrong?
$.post is a shortcut for an AJAX call, AJAX is by definition asynchronous, which means it won't wait on a response before continuing processing. If you switch it to a regular AJAX() method, there is an async option you can set to false, which will make it behave as you are expecting.
Alternatively you can also define a function to execute on successful return of the AJAX request, in which you can call the next step in your process chain.
The AJAX call is asychronous; that means that the callback method exposes by $.post will be executed when the request completes, but your javascript will continue executing as soon as the invoke to $.post finishes. If you want to do something after the ajax call is done, you need to provide a callback method and do something else, ex:
MyLibrary.fn.CheckAndSendAddForm = function (txtName, ImageUrl, callback) {
var postUrl = "/admin/add";
var persistedObject = new Object();
$.post(
postUrl,
{ Name: txtName, ImageUrl: txtImageUrl},
function (data) {
if (data.Status == 200) {
console.log("Post status:" + data.Message);
persistedObject.isPersisted = true;
persistedObject.gdEntityId = data.Data;
} else if (data.Status == 500) {
console.log("Failed to post entitiy");
} else {
console.log("Fault with Javascript");
}
callback(); // This is where you return flow to your caller
}, "json"
);
};
Then you invoke like so:
var persistedObject = MyLibrary.fn.CheckAndSendAddEntityForm("name", "avatarurl.png", function()
{
console.log("test");
//check if persisted and go to next step
if (persistedObject.isPersisted) {
MyLibrary.fn.InitAddAnotherEntityForm(persistedObject .gdPronoId);
} else {
alert("Oops, something went wrong. Please call 911");
}
});
JavaScript is single-threaded. If you have asynchronous functionality, a simple boolean semaphore variable will help not to allow invocations of a function while some processes are running.
If you want to execute asynchronous tasks one by one (like a domino line), you will need to use callback functions.
What you're encountering is the "asynchronous" bit of AJAX. If you want to physically (as in the line line by line in the Javascript file) you can use the .success,.pipe or .done jQuery methods to add a callback to process the data further. Don't embed your callbacks if you can help it, or you will get a "domino effect" as you call it.
I'm a novice-to-intermediate JavaScript/jQuery programmer, so concrete/executable examples would be very much appreciated.
My project requires using AJAX to poll a URL that returns JSON containing either content to be added to the DOM, or a message { "status" : "pending" } that indicates that the backend is still working on generating a JSON response with the content. The idea is that the first request to the URL triggers the backend to start building a JSON response (which is then cached), and subsequent calls check to see if this JSON is ready (in which case it's provided).
In my script, I need to poll this URL at 15-second intervals up to 1:30 mins., and do the following:
If the AJAX request results in an error, terminate the script.
If the AJAX request results in success, and the JSON content contains { "status" : "pending" }, continue polling.
If the AJAX request results in success, and the JSON content contains usable content (i.e. any valid response other than { "status" : "pending" }), then display that content, stop polling and terminate the script.
I've tried a few approaches with limited success, but I get the sense that they're all messier than they need to be. Here's a skeletal function I've used with success to make a single AJAX request at a time, which does its job if I get usable content from the JSON response:
// make the AJAX request
function ajax_request() {
$.ajax({
url: JSON_URL, // JSON_URL is a global variable
dataType: 'json',
error: function(xhr_data) {
// terminate the script
},
success: function(xhr_data) {
if (xhr_data.status == 'pending') {
// continue polling
} else {
success(xhr_data);
}
},
contentType: 'application/json'
});
}
However, this function currently does nothing unless it receives a valid JSON response containing usable content.
I'm not sure what to do on the lines that are just comments. I suspect that another function should handle the polling, and call ajax_request() as needed, but I don't know the most elegant way for ajax_request() to communicate its results back to the polling function so that it can respond appropriately.
Any help is very much appreciated! Please let me know if I can provide any more information. Thanks!
You could use a simple timeout to recursively call ajax_request.
success: function(xhr_data) {
console.log(xhr_data);
if (xhr_data.status == 'pending') {
setTimeout(function() { ajax_request(); }, 15000); // wait 15 seconds than call ajax request again
} else {
success(xhr_data);
}
}
Stick a counter check around that line and you've got a max number of polls.
if (xhr_data.status == 'pending') {
if (cnt < 6) {
cnt++;
setTimeout(function() { ajax_request(); }, 15000); // wait 15 seconds than call ajax request again
}
}
You don't need to do anything in your error function unless you want to put an alert up or something. the simple fact that it error will prevent the success function from being called and possibly triggering another poll.
thank you very much for the function. It is a little bit buggy, but here is the fix. roosteronacid's answer doesn't stop after reaching the 100%, because there is wrong usage of the clearInterval function.
Here is a working function:
$(function ()
{
var statusElement = $("#status");
// this function will run each 1000 ms until stopped with clearInterval()
var i = setInterval(function ()
{
$.ajax(
{
success: function (json)
{
// progress from 1-100
statusElement.text(json.progress + "%");
// when the worker process is done (reached 100%), stop execution
if (json.progress == 100) clearInterval(i);
},
error: function ()
{
// on error, stop execution
clearInterval(i);
}
});
}, 1000);
});
The clearInterval() function is becomming the interval id as parameter and then everything is fine ;-)
Cheers
Nik
Off the top of my head:
$(function ()
{
// reference cache to speed up the process of querying for the status element
var statusElement = $("#status");
// this function will run each 1000 ms until stopped with clearInterval()
var i = setInterval(function ()
{
$.ajax(
{
success: function (json)
{
// progress from 1-100
statusElement.text(json.progress + "%");
// when the worker process is done (reached 100%), stop execution
if (json.progress == 100) i.clearInterval();
},
error: function ()
{
// on error, stop execution
i.clearInterval();
}
});
}, 1000);
});
You can use javascript setInterval function to load the contents each and every 5 sec.
var auto= $('#content'), refreshed_content;
refreshed_content = setInterval(function(){
auto.fadeOut('slow').load("result.php).fadeIn("slow");},
3000);
For your reference-
Auto refresh div content every 3 sec