I want to have users log in with a 6 digit code they get from an Authentication app.
So far I figured out how to check wether the code is the same in the app and online.
It says "454 343" in the Google Authentication app, and by going to https://authenticatorapi.com/validate.aspx?Pin=454343&SecretCode=1234
I will get a response saying either True or False.
How exactly do I take that "True" or "False" and set it as a variable in JS?
Try this:
(async () => {
const
url = 'https://authenticatorapi.com/validate.aspx?Pin=454343&SecretCode=1234',
response = await fetch(url),
answer = await response.text() === 'True';
console.log(answer);
})();
What you are looking for is sending a HTTP request to that url which tells you if the code is correct.
Previously, you would use XMLHttpRequest but that is becoming somewhat dated.
Instead, I recommend using the fetch API:
(async () => {
var response = await fetch("https://authenticatorapi.com/validate.aspx?Pin=454343&SecretCode=1234");
console.log(response.text()); // should print out "True" or "False"
})();
For more information, check out
Note that the code is inside an async function because fetch() is asynchronous
this is how I would do it,
var test = new Promise (resolve => {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
resolve(xhttp.responseText)
}
};
xhttp.open("GET", "https://authenticatorapi.com/validate.aspx?Pin=454343&SecretCode=1234", false);
xhttp.send();
})
test.then(result => {
console.log(result)
// expected result is boolean
})
Related
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
async/await implicitly returns promise?
(5 answers)
Closed 1 year ago.
In the below example from https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/onreadystatechange, I am trying to store the responseText in a variable that can be accessed outside of the onreadystatechange function.
const xhr = new XMLHttpRequest(),
method = "GET",
url = "https://developer.mozilla.org/";
xhr.open(method, url, true);
xhr.onreadystatechange = function () {
// In local files, status is 0 upon success in Mozilla Firefox
if(xhr.readyState === XMLHttpRequest.DONE) {
var status = xhr.status;
if (status === 0 || (status >= 200 && status < 400)) {
// The request has been completed successfully
console.log(xhr.responseText);
} else {
// Oh no! There has been an error with the request!
}
}
};
xhr.send();
At the moment, the only way I know to handle the return value from a function is to do the following
const variable = function()
But I can't figure out how to make that won't work due to the way the onreadystatechange event handler is initiated/called. I tried declaring a variable outside of the function scope, but it keeps returning 'undefined'.
Thanks for your help.
*Edit for async/await with fetch
I've been trying to implement the async/await fetch option but can't seem to get it to work right. I've been checking the output with alerts and the alert located outside the function always fires before the one inside the function. As a result the one outside is giving me "object Promise" and the one inside is the actual data. I'm returning the data but it's just not working.
const getData = async function (filePath) {
let myData
await fetch(filePath)
.then(response => response.text())
.then(data => myData = data)
alert(myData)
return (myData)
}
let theData = getData('./2021WeatherData.csv')
alert(theData)
So "alert(theData)" fires before "alert (myData)" and I can't understand why. Thanks so much.
This is a good question.
The XMLHttpRequest is code that runs asynchronous. And it can't run synchronously as the yellow warning on the Mozilla developer documentation explains.
So what does that mean?
(Pseudocode)
var result
console.log('start')
xhr.onreadystatechange = function() {
result = '5'
console.log('request done')
}
console.log('result', result)
This will output as you already noticed
start
result undefined
request done
and you are expecting:
start
request done
result 5
But this does not work, because the console.log('result', result) was
already executed before the variable was assigned.
Solutions
Using a callback
Whatever you want to do with the result, you need to do within the callback function. Like:
Console.log the value
Updating the browser dom (I added this as an example)
Store to the database on a node server
The callback function is the function you are assigning to the onreadystatechange.
(Pseudocode)
xhr.onreadystatechange = function() {
// This code is the callback function.
// You can do everything with the result, like updating the DOM,
// but you can't assign it to a variable outside.
}
Actual code
function logResultToTextBox(result) {
document.getElementById('#server-result-text-box').value = result
}
const xhr = new XMLHttpRequest(),
method = "GET",
url = "https://developer.mozilla.org/";
xhr.open(method, url, true);
xhr.onreadystatechange = function () {
// In local files, status is 0 upon success in Mozilla Firefox
if(xhr.readyState === XMLHttpRequest.DONE) {
var status = xhr.status;
if (status === 0 || (status >= 200 && status < 400)) {
// The request has been completed successfully
console.log(xhr.responseText);
// DO WHATEVER YOU NEED TO DO HERE WITH THE RESULT, BUT YOU CANT ASSIGN THE VALUE TO VARIABLE WHICH IS DECLARED OUTSIDE
logResultToTextBox(xhr.responseText);
} else {
// Oh no! There has been an error with the request!
}
}
};
xhr.send();
Wrap it using a promise
The promise is another way on how you can solve this. You wrap it into a promise function. Not that the promise has no return statement. It uses a resolver.
The actual work will then be done after you define the functions. It will be called in the then block.
Here you also can't assign a value to a variable that is declared outside.
Pseudocode:
Promise makeRequest(url) {
// doRequest to url and resolve result
}
makeRequest('http://example.com).then((result) => {
// do whatever you want with the result
})
Actual code:
function logResultToTextBox(result) {
document.getElementById('#server-result-text-box').value = result
}
function makeRequest (method, url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.status,
statusText: xhr.statusText
});
};
xhr.send();
});
}
// Example:
makeRequest('GET', 'https://developer.mozilla.org/')
.then(function (result) {
console.log(result);
logResultToTextBox(result)
})
.catch(function (err) {
console.error('Augh, there was an error!', err.statusText);
});
Use async / await
For a few years, there is also a new syntax called async/await. This gives you the possibility to write code like it would a normal code.
Pseudocode
let myServerResult;
async function doAsyncCall(url) {
const serverResult = await callServer(url);
return serverResult
}
myServerResult = await doAsyncCall('http://example.com')
console.log(myServerResult)
In that case, I would use fetch instead of XMLHttpRequest because it is much simpler.
Actual code:
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1');
const jsonResult = await response.json();
console.log(jsonResult)
(note that I am using jsonplaceholder.typicode.com as an API as the HTML output of the Mozilla documentation does not work with it. If you really need XMLHttpRequest then have a look into the answers on this question)
P. S. Don't use old synchronous code syntax
There is also an old-school way by using "Synchronous request". But this should not be used anymore, as it leads to a bad user experience because it makes the loading of the website slow.
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests#synchronous_request
Answer after the question edit
You need to await the following line
instead of
let theData = getData('./2021WeatherData.csv')
you need to write
let theData = await getData('./2021WeatherData.csv')
I have a twilio Javascript function that gets executed in my studio flow immediately after somebody calls the associated studio flow phone #. This function is supposed to check if there is a currently active conference call going on and return either "True" or "False" so that I can then use that string in an if/else widget to either connect the caller OR initiate a new conference.
// This is your new function. To start, set the name and path on the left.
exports.handler = function(context, event, callback) {
var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.open("GET", "https://api.twilio.com/2010-04-01/Accounts/myAccountSid/Conferences.json?FriendlyName=mySidDocumentName");
xhr.setRequestHeader("Authorization", "Basic myAuthString");
xhr.send();
xhr.addEventListener("readystatechange", function() {
if(this.readyState == 4) {
console.log(JSON.stringify(xhr.responseText));
var jsonResponse = JSON.parse(xhr.responseText);
var arrayLength = Object.keys(jsonResponse.Conferences[jsonResponse]).length;
if (arrayLength > 0) {
var isConferenceOngoing = "True"
} else {
var isConferenceOngoing = "False"
}
}
return callback(null, isConferenceOngoing);
});
};
The "conferences" key that I am interested in, in the response, is an array and that causes a problem because Twilio can not parse an array in the studio flow, so it has to be done in the function call: https://www.twilio.com/docs/studio/widget-library/http-request "Note that, although an array is valid JSON, if your request returns an array of objects, it will not be parsed."
So all I simply need to check for is whether or not the Conferences array is empty and if so return "False" to my studio flow OR if there is an active conference (i.e. the array length is > 0) then return "True". Returning either "True" or "False" will allow me to do an if/else widget in my studio flow to either connect the caller to the existing conference or start a new conference call.
Here is what the response looks like in Postman when there is not an active conference call (notice the conferences array is empty):
My knowledge of Javascript is next to zero, but I think I am close.
Update: Added async/await functions based on comments.
exports.handler = function(context, event, callback) {
const twilioClient = context.getTwilioClient();
let responseObject = twilioClient.conferences
.list({status: 'in-progress'})
var isConferenceOngoing = null
async function setConferenceVariable() {
if (responseObject.Length > 0)
{
isConferenceOngoing = "True"
} else {
isConferenceOngoing = "False"
}
}
async function getConferenceDetails() {
await setConferenceVariable();
return callback(null, isConferenceOngoing);
}
getConferenceDetails()
};
Latest Update: Simpler method below.
exports.handler = function(context, event, callback) {
const twilioClient = context.getTwilioClient();
twilioClient.conferences
.list({status: 'in-progress'})
.then(conferences => { callback(null, !conferences.length)})
};
Latest update looks much better #dylan. I am not seeing a return value when false, so did this:
exports.handler = function(context, event, callback) {
const twilioClient = context.getTwilioClient();
twilioClient.conferences
.list({status: 'in-progress'})
.then(conferences => callback(null, {result: !conferences.length}))
.catch(err => callback(err));
};
I am new to jQuery and I am trying to build something with AJAX. I tried to use the normal, basic XMLHttpRequest class and then $.ajax({...}) but none of them work.
function getNormal_XHR_Request(){
let request = new XMLHttpRequest();
// I also tried to make a variable here, assign it to the request.response and return it in the end
let returnValue = null;
request.addEventListener(
"load",
(event) => {
if(request.readyState === 4 && request.status === 200){
console.log(request); // this will work
console.log(request.response); // this will work too
// Assign the returnValue to the request.response
returnValue = JSON.parse(request.response); // JSON.parse(request.response) doesn't return undefined, it returns my object that I want to use, but even if I equal the returnValue to it, it will still return undefined
return JSON.parse(request.response);
}
},
false // dispatch event in the bubbling phase not in the capturing phase
)
request.open("GET", "scripts/users.json");
request.setRequestHeader("Accept", "text/json");
request.send();
return returnValue
}
This is the normal XMLHttpRequest() that I used. The problem is, that if I want to say for example
x = getNormal_XHR_Request()
so that I would have the JSON file in my x variable, it doesn't work and it returns undefined.
And the exact same things happens with $.ajax({...})
class XHR_REQUEST{
constructor(path){
this.path = path;
}
getRequest_XHR(requestHeaders){
this.requestHeaders = requestHeaders;
var self = this;
let returnValue = [];
$.ajax({
url : self.path,
dataType : "json",
headers : self.requestHeaders,
})
.done((data) => {
returnValue = data;
})
.fail((jqXHR, errorMessage, error) => {
console.log(jqXHR);
console.log(errorMessage);
console.log(error);
});
return returnValue;
}
};
It works the same as the normal function that I used above, everything that I do, returns undefined, even if request.response gives me the correct answer. If I try to console.log(request.response), I will get the object, if I try to return it and console.log() the result, it will give me back undefined.
Why ?
The result you are getting in both examples is completely normal. The reason you are getting an undefined value is because both requests are happening asynchronously. The correct place where to return something or take some action with the results you get are:
1) request.addEventListener('load', event => {} .... within the event here you can perform the action on a succesfull response.
2) with the $.ajax() call you do it within the done() promise handler.
Those are the right places to take proper action on the results. You have to modify your mindset to start using the responses from the asynchronous callbacks.
The problem there is that you are returning the result, even before the callback has some value filled in the variable.
There are new constructions this days to make asynchronous calls behave synchronously withe async-await constructions. But besides this I think you have to modify or adjust your code to react to the async results in a way it works.
Im currently trying to utilize multiple ajax calls through ajax chaining, but unsure on the best approach since there are a few ways to do this via new frameworks, jquery and pure javascript.
I would prefer to do this with pure vanilla javascript given the native development on js has improved a lot over recent years, however, in the instance of multiple ajax calls, i believe there is still plenty to be improved upon, i believe one of the ways would be to use promises ? i do see many deal with this occurrence via jquery.
i would be very appreciative if fellow coders could give their example as to how they would code a modern approach ajax chain call dependent on ajax returned call values preceding it.
ok, so in short, i am attempting to pass the value of the first ajax call to the second ajax call, along with identifying the correct way of executing the second ajax call.
Below i have added code with comments:
// Establish functionality on window load:
window.onload = function() {
'use strict';
// get product id on load
var pid = document.getElementById('pid');
var colorlist = document.getElementById('colorlist');
var sizelist = document.getElementById('sizelist');
colorlist.onclick = function(e) {
if (typeof e == 'undefined') e = window.event;
var colorid = e.target.value
while (sizelist.firstChild) {
sizelist.removeChild(sizelist.firstChild);
}
// 2ND AJAX CALL
var xhr = getXMLHttpRequestObject();
xhr.open('GET', '/ajax/get_sizes.php?id=' + encodeURIComponent(pid.value) + '&colorid=' + encodeURIComponent(colorid), true);
// set header if sending to php
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(null);
// Function to be called when the readyState changes:
xhr.onreadystatechange = function() {
// Check the readyState property:
if (xhr.readyState == 4) {
// Check the status code:
if ( (xhr.status >= 200 && xhr.status < 300)
|| (xhr.status == 304) ) {
var sizes = xhr.responseText;
var sizes = JSON.parse(sizes);
for (var i = 0, num = sizes.length; i < num; i++) {
var label = document.createElement('label');
label.setAttribute ("for", sizes[i].id);
label.classList.add("swatch");
label.innerHTML = sizes[i].size;
var radio = document.createElement('input');
radio.type = "radio";
radio.id = sizes[i].id;
radio.value = sizes[i].id;
radio.name = "sizes";
sizelist.appendChild(label);
sizelist.appendChild(radio);
} //END OF FOR LOOP
} else { // Status error!
document.getElementById('output').innerHTML = xhr.statusText;
}
} // End of readyState IF.
}; // End of onreadystatechange anonymous function.
}; // END OF COLORLIST ONCLICK
// 1ST AJAX CALL
var ajax = getXMLHttpRequestObject();
// Function to be called when the readyState changes:
ajax.onreadystatechange = function() {
// Check the readyState property:
if (ajax.readyState == 4) {
// Check the status code:
if ( (ajax.status >= 200 && ajax.status < 300)
|| (ajax.status == 304) ) {
var colors = ajax.responseText;
var colors = JSON.parse(colors);
for (var i = 0, num = colors.length; i < num; i++) {
var label = document.createElement('label');
label.setAttribute ("for", colors[i].id);
label.classList.add("swatch", colors[i].color);
label.innerHTML = colors[i].color;
var radio = document.createElement('input');
radio.type = "radio";
radio.id = colors[i].id;
radio.value = colors[i].id;
radio.name = "colors";
colorlist.appendChild(label);
colorlist.appendChild(radio);
} // END OF FOR LOOP
} //END OF STATUS CODE CHECK
else { // Status error!
document.getElementById('output').innerHTML = ajax.statusText;
}
} // End of onreadyState IF.
}; // End of onreadystatechange anonymous function.
ajax.open('GET', '/ajax/get_colors.php?id=' + encodeURIComponent(pid.value), true);
// set header if sending to php
ajax.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
ajax.send(null);
}; // End of onload anonymous function.
Regards David
Welcome to SO and thank you for your question. I'll do my best to show you some examples of how you could execute your code in a way that might be preferable to you as a solution.
Callbacks
What is a callback?
Simply put: A callback is a function that is to be executed after another function has finished executing — hence the name ‘call back’.
Source of quote
In your code example you want to execute at least 2 HTTP request after one another. This would mean that a piece of code has to be executed twice. For this you can write a function around the XMLHTTPRequest piece to be able to execute it multiple times when writing it down only once.
The function below here has two parameters: url and callback. The url parameter is a string which will be injected in the second parameter of the xhr.open method. The callback parameter will be a function. This function will be called when the request has been succesfully finished.
function get(url, callback) {
var xhr = new XMLHTTPRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
if ('function' === typeof callback) {
callback(xhr.responseText);
}
}
};
xhr.open('GET', url, true)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send();
}
Here is a little example of how it would work. See that the callback function has a parameter called data1. That is the xhr.responseText that we got back from the XMLHTTPRequest. Inside the callback function call the get function again to make another request.
get('/ajax1.php', function(data1) {
// Do something with data1.
get('/ajax2.php', function(data2) {
// Do something with data2.
});
});
This is a fairly simple way to make a request after another is finished.
But what if we have 100 requests after each other?
Promisified XMLHTTPRequest
The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.
Source of quote
Enter Promises. The example below here is almost the same as the example above. Only this time we use a Promise. When calling get we immediately return a Promise. This promise will wait for itself to either resolve or reject. In this case we only use resolve for successful requests. Whenever the request has finished resolve is called and the Promise chain begins.
function get(url) {
return new Promise(resolve => {
var xhr = new XMLHTTPRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
if ('function' === typeof callback) {
resolve(xhr.responseText);
}
}
};
xhr.open('GET', url, true)
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send();
});
}
So instead of using a callback function we use then. Inside then we do use a callback function which allows us to use the value that has been resolved in the returned Promise. then can be chained with more then's inifinitely until you run out of things to chain.
Inside the callback function call the next request.
get('/ajax1.php')
.then(data1 => {
// Do something with data1.
get('/ajax2.php')
.then(data2 => {
// Do something with data2.
});
});
Fetch
The Fetch API provides an interface for fetching resources (including across the network). It will seem familiar to anyone who has used XMLHttpRequest, but the new API provides a more powerful and flexible feature set.
Source of quote
Before we've created our own Promise version of an XMLHTTPRequest. But JavaScript has evolved an has gotten new tools to work with. fetch kind of looks like how our get function works, but has way more features and options to make it more powerful. And it also uses promises!
fetch('/ajax1.php')
.then(response1 => response1.text())
.then(data1 => {
// Do something with data1.
fetch('/ajax2.php')
.then(response2 => response2.text())
.then(data2 => {
// Do something with data2.
});
})
.catch(error => console.log(error));
Though the then syntax still makes us nest functions. Like before, what about when you have 100 callback functions to call? That will be a nesting mess!
Fetch + Async/Await
Now this is the way to tackle the nesting problem and make the syntax more like assigning a simple variable in JS. Async/Await is a part of modern JavaScript, so be wary of older browsers not supporting it. Check caniuse for the current support.
The Async/Await syntax works like the following. Create a function with the async keyword in front of it. This will indicate, like it implies, that async code will be performed here. It also makes the function with async before it automatically return a Promise.
Inside the async function use the await keyword whenever you call a function which returns a Promise, like fetch, or our own function get. This will return the resolved value without having to use then or a callback function.
The await keyword also makes the code actually wait before continueing to the next line of code. Now your JS looks nice and can be written with less nesting.
(async function() {
const response1 = await fetch('/ajax1.php');
const data1 = await response1.text();
// Do something with data1.
const response2 = await fetch('/ajax2.php');
const data2 = await response2.text();
// Do something with data1.
}());
I really hope this is helpful and helps you get where you need to be going.
If you have any questions regarding the above, please let me know!
Have a good one!
You can use Promise chain which could be like the example mentioned below:
.then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);enter code here
You can the documentation mentioned below:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
I am trying to create a function that returns the contents of a txt file using JavaScript so it can be stored in a variable and used at a later time. So far I have tried:
var text;
function getText(url) {
fetch(url)
.then( r => r.text() )
}
getText("foo.txt").then(r => text);
but this says that text is undefined.
I have also tried:
function getText(url) {
var client = new XMLHttpRequest();
client.open('GET', url);
client.onreadystatechange = function() {
text = client.responseText;
return text;
}
client.send();
}
This still gives me undefined. If I put a console.log() inside the readystatechange it gives the right text but repeats it twice.
I have also tried:
function getText(url) {
jQuery.get(url, function(data) {
console.log(data);
});
return data;
}
but this gives me an error saying jquery is not defined and I can't seem to get jQuery to work so preferably is there anyway to do this without jquery?
The first approach using the fetch API is rightly returning undefined as you are not returning the thenable. Note that the return value itself is a promise and not the data, you need to call then() to log the data. Currently there is no way to assign the returned data directly to a variable.
As for the second example if you put the console.log in the client.onreadystatechange of course it will log the response twice as it gets called multiple times for a single request. You should only log the response when the response code is 200 OK and state is complete with a value of 4.
function getText(url) {
return fetch(url) //notice the return, it is returning a thenable
.then(text => text.text())
}
getText("https://jsonplaceholder.typicode.com/todos/1").then(d => console.log(d));
//Using Async/Await
async function getTextAsync(url) {
let text = await fetch(url)
let data = await text.text()
return data;
}
getTextAsync("https://jsonplaceholder.typicode.com/todos/1").then(d => console.log(d));
//Using XMLHttpRequest
function getTextXml(url) {
var client = new XMLHttpRequest();
client.open('GET', url);
client.onreadystatechange = function() {
if (client.readyState == 4 && client.status == 200) //notice the response code and the state code
{
if (client.responseText)
{
console.log(client.responseText);
}
}
}
client.send();
}
getTextXml("https://jsonplaceholder.typicode.com/todos/1");
console.log() just prints it to the console, is there anyway to store it in the variable?
Because fetch is an async process you will never be able to assign a value to text like that. The closest you can get with modern browsers at the moment is to use async/await.
// Mock `fetch` to return a value after 1.5s
function getText(url) {
return new Promise(resolve => {
setTimeout(() => resolve('Hallo'), 1500);
});
}
// Unfortnately `await` has to be used in conjunction
// with async so we have to use an immediately-invoked
// async function expression here
(async () => {
// `await` for the promise to resolve
const text = await getText("foo.txt");
console.log(text);
})();
<p>Please wait 1.5s</p>
There's a lot more information (which will take a while to distill) in the answers to this Stackoverflow question.