synchronous fetch - unexpected results - javascript

First of all, I'm aware this is not a good approach, need it as temporary solution for certain functions to return value, not promise. I know it's really not good permanent solution at all, but I need it for now.
What worries me, fetch sure finishes sooner - but it runs until the whiles times out, and then to console comes first the RETVAL false, and only then second line comes RETFETCH: {....} with returned json values - it seems the 'haveResponse' value does not change in the second 'then' - but can't see why, and how to bypass it.
It's a temporary workaround for old sync fns to read some data from remote service running on local pc on some port, but for now I can't rewrite the function which expects to receive data from this fn, so there must be no promise on the outside, need to wait for response and then return it.
function syncFetch(url) {
var haveResponse = false;
var reqtime = new Date();
try{
fetch(url, {
headers: {'Content-Type': 'application/json'},
method: 'POST',
timeout: 1500,
body: JSON.stringify({cmd:'init'})
})
.then(response => response.json())
.then(data => {
console.log('RETFETCH:', data);
haveResponse = data;
return data;
});
// timeout
while (haveResponse === false) {
var endDate = new Date();
if (haveResponse !== false) { return haveResponse; }
if ((endDate - reqtime)/1000 > 5) { // max 5 sec
return haveResponse;
}
}
return haveResponse;
} catch(e){
console.log('error', e);
haveResponse = -1;
}
return haveResponse;
}
console.log('RETVAL',syncFetch('http://127.0.0.1:3333/'));

Save yourself a few headaches, drop all the .then() and use the async/await syntax instead. No need for dirty timeout/while hacks.
I renamed syncFetch to asyncFetch, because your original code was never synchronous in the first place (and that's precisely why you are struggling so much, you believe it is when it's not). async/await don't make the code synchronous either. It's just awesome syntactic sugar around Promises.
(EDIT : you said in the comments that you can't add the async keyword to the parent function (asyncFetch), so here's a workaround to place it inside :
function asyncFetch(url) {
async function a() {
try {
const response = fetch(url, {
headers: { 'Content-Type': 'application/json' },
method: 'POST',
timeout: 1500,
body: JSON.stringify({ cmd: 'init' })
});
const data = await response.json();
return data; // This is a Promise<data>, not data directly, so it needs to be awaited
} catch (e) {
console.log('error', e);
return null
}
};
return a();
};
(async () => {
console.log('RETVAL', await asyncFetch('http://127.0.0.1:3333/')); // Needs to be awaited, and therefore also needs to be inside an async function
})();

Related

How to execute asynchronous code in Javascript

I've looked into MDN web docs article on async functions and for some reason it doesn't work for me
Here's my code
function createObject() {
try {
console.log("Processing POST Loan.");
var data = {
"value1" : 1288,
"value2" : [{
"value3" : 3833,
"value4": [{
"value5": new Date()
}]
}]
}
var auth = Buffer.from("login:pass").toString('base64')
const res = request("http://url.com/resource", {
method: "POST",
headers: {
'Content-Type': 'application/json',
'Authorization': "Basic "+auth
},
body: JSON.stringify(data)
}, function(error, response, body){
//console.log(response + ' '+body)
})
var response = res.body.id;
return response
}
catch(err) {
throw err
}
}
async function uploadReport() {
console.log('cerate object')
var objectId = await createObject();
console.log('object = '+objectId)
}
uploadReport()
Exactly as described in the article. But when I run my script I get this result:
cerate object
Processing POST Loan.
'data {"value1":1288,"value2":[{"value3":3833,"value4":[{"value5":"2021-10-05T09:45:46.126Z"}]}]}'
'auth '
object = undefined
Then nothing happens for a few seconds and execution stops. Http request works fine and objects are being created as I run the script, though I don't get any response (this API should return auto generated object ID). What am I doing wrong here?
I'm assuming you are quite new to JS programming and it seems you lack a little understanding about how the flow of execution works for async code.
In JS, async code works in promises. Promises are the way JS represents code that eventually will yield a value (or void, but it's the same).
So now we need a way to control the flow of execution. This means, we should be able to execute code that depends on that result after we have gotten that result. Or in other words, we should wait for the promise to resolve and then execute the code that depended on it.
Enter callbacks. Callbacks is how this is done in JS (before async/await appeared, but let's not get ahead of ourselves). As functions are first class citizens in JS we can declare them and pass them around as function arguments. So, in those terms, a callback it's the code that should be executed after the promise has given us its result.
When it comes to callbacks, me personally I have seen two ways of dealing with them.
A first approach, function expects such callback as an argument. Usually (but it doesn't have to be like this) the first arguments are the actual arguments necessary to perform the task, and the last one is the callback: what to do after the task is done. Example:
// A function that receives an `arguments` object and a `callback` function.
// Observe
function doSomethingAsynchronous(arguments, callback) {
const a = arguments.a
const b = arguments.b
const result = a + b //this is not asynchronous, only to illustrate.
callback(result)
}
You would use this function like this:
doSomethingAsynchronous({a:2, b:3}, (result)=>{
console.log(`The result was ${result}`)
})
Note how doSomethingAsynchronous does not return anything; the flow of execution is directed towards the callback.
A second approach might be a function that returns an actual Promise. Promises have a then() method and a catch() method. These are used to chain more code after the resolution of the Promise:
function iReturnAPromise(arguments) {
return new Promise((resolve, reject) => {
const result = arguments.a + arguments.b;
resolve(result);
})
}
You would manage the flow of execution by doing so:
const promiseOfTheResult = iReturnAPromise({a: 2, b:2})
promiseOfTheResult.then((result) => {console.log(result)})
// You'll never see it like that. You'll always see:
iReturnAPromise({a:2, b:3}).then((result) => {console.log(result)})
And last, but definitely not least, came async/await which simplified the use of promises. With async await, you would declare iReturnAPromise just the same, but you would use it like so;
const result = await iReturnAPromise({a:1, b:2})
console.log(result)
Notice how this last method keeps the code in line and avoids the callback hell. Also notice how functions that don't return a promise cannot be awaited, they first have to be "promisified": that is, wrapping them in a promise.
Let's start from the begining since you seem a little confused.
What you want to do is to make an async call from a callback kind of function ('request'). For that, you must use Promise.
So your createObject function must return a new Promise object, or must be declared as an async function. In your case, since the request function use a callback pattern, you have to use a Promise object because it will provide you a callback that must be called when your Promise resolve.
function createObject() {
return new Promise((resolve, reject) => {
try {
console.log("Processing POST Loan.");
var data = {
"value1" : 1288,
"value2" : [{
"value3" : 3833,
"value4": [{
"value5": new Date()
}]
}]
}
var auth = Buffer.from("login:pass").toString('base64')
const res = request("http://url.com/resource", {
method: "POST",
headers: {
'Content-Type': 'application/json',
'Authorization': "Basic "+auth
},
body: JSON.stringify(data)
}, function(error, response, body){
if(error) reject(error);
resolve({ response, body });
});
}
catch(err) {
reject(err);//the promise is rejected
}
});
}
async function uploadReport() {
console.log('cerate object')
const res = await createObject();
//in res you get 'response' and 'body' where you have the result of your API.
//it's up to you to adapt this to what you want
console.log(res);
}
uploadReport()

My asynchronous javascript gets executed in the middle of the other function

Im trying to execute a function after the other one in Vue.js. I've already tried async/await, callback functions, .then, but it somehow doesnt want to load one after the other. What is a possible solution?
auth_mixin.js:
async auth () {
console.log("authban")
var token = this.getCookie("token")
var jsonData = {}
jsonData["token"] = token
console.log(jsonData)
var bodyFormData = new FormData();
bodyFormData.append('data', JSON.stringify(jsonData));
axios({
method: 'post',
url: 'backend/index.php?action=checkAuth',
data: bodyFormData,
headers: {'Content-Type': 'multipart/form-data'}
})
.then(function (response) {
console.log(response);
if(response.data.status==="OK"){
console.log("ok")
return true;
}else{
console.log("nem ok")
return false;
}
})
.catch(function (response) {
console.log(response);
return false;
});
}
Navbar.vue:
created () {
var result=false
this.auth().then(this.checkIfLoggedIn(result))
},
methods: {
checkIfLoggedIn (isLoggedIn) {
console.log("na ez lesz az erdekes "+isLoggedIn)
if(isLoggedIn === true){
console.log("true")
document.getElementById("logged_out").style.display="none";
document.getElementById("logged_in").style.display="block";
}else{
console.log("fail");
}
}
}
this.auth().then(this.checkIfLoggedIn(result))
You have two problems.
First: this.checkIfLoggedIn(result) calls checkIfLoggedIn immediately. You need to pass a function to then.
this.auth().then(() => this.checkIfLoggedIn(result))
Second: With that change, you call checkIfLoggedIn when auth resolves.
So when does auth resolve? Well, it is defined with the async keyword, so it resolves when it returns (unless it returns a promise, in which case it adopts that promise instead).
So what does it return? It has no return statement, so it returns undefined when it gets to the end … which is immediately after the call to axios (since you aren't awaiting that).
If you returned the return value of axios(...).etc then it wouldn't resolve until that promise resolved.
(Aside: You're using async, you should probably refactor to use await, try {} catch() {} instead of .then() and .catch()).

How to properly implement async/await

I am new to using async/await - I am trying to return data from an API call and format/tidy it slightly.
I'm really struggling to work out how to make this work because of the asynchronous nature of the functions. I cannot get the promise to work without the browser simply falling over.
My first function calls the API and gets a response as JSON. I then store a subset of this data json.recommendations
function getRecs() {
const requestUrl = `blahblah`;
const options = {
headers: {
'Content-type': 'application/json',
Accept: 'application/json',
},
method: 'GET',
};
fetch(requestUrl, options).then((res) => {
if (res.ok) {
return res.json();
}
throw new Error('Error!!??', res);
}).then((json) => {
return json.recommendations;
});
}
My second function takes json.recommendations and does some tidying to remove unwanted data and return a new array of data, those that match my filter.
async function getInStockRecs() {
const recs = await getRecs();
if (recs !== undefined) {
return recs.filter(function(rec){
return rec.isInStock === true;
});
}
}
A third function formats the data further:
async function topThreeArray() {
const inStockRecs = await getInStockRecs();
const topThree =[];
for (let i = 0; i < i <= 3; i++) {
topThree.push(inStockRecs[0]);
}
return topThree;
}
By using await I intended each function to only run once the data has been returned properly from the previous. However running the above crashes the page and I cannot do anything to debug as it simply crashes. Where am I going wrong?
You don't return anything in your getRecs() function (you only return in the callbacks to the fetch() call)
Since you're using async-await elsewhere, why not use that for the getRecs() function too?:
async function getRecs() {
const requestUrl = `blahblah`;
const options = {
headers: {
'Content-type': 'application/json',
Accept: 'application/json',
},
method: 'GET',
};
const res = await fetch(requestUrl, options);
if (res.ok) {
return res.json().recommendations;
}
throw new Error('Error!!??', res);
}
Otherwise, you'd have to return the fetch() call itself:
return fetch(requestUrl, options).then((res) => {
...
The reason the browser crashes is because the condition in the for loop in topThreeArray() is weird (i < i <= 3) and results in an infinite loop.
Basically, i < i evaluates to false, which gets implicitly coerced into 0, so the condition effectively becomes 0 <= 3 which is always true.
Finally, I'd like to point out that you should carefully consider if async-await is appropriate in the first place when running in a browser as support for it is still very fragile and scetchy in browsers.

JavaScript/React - returning Promise inside a function

I'm having a little trouble dealing with some Promises in my app, any clarification would be much appreciated.
I've been building a Phoenix/React app loosely based on this tutorial - https://medium.com/#benhansen/lets-build-a-slack-clone-with-elixir-phoenix-and-react-part-3-frontend-authentication-373e0a713e9e - and I'm trying to restructure my code a bit to make it easier for me to build out other aspects of the app in the future.
Initially, when posting login data to my Phoenix server, the function that I was using looked like this (from Login.jsx):
fetch(`${apiUrl}/sessions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({person: person})
}).then(response => {
this.setState({loadingData: false}, () => {
response.json().then(result => {
if(result.status === "error"){
this.setState({error: {isError: true, message: result.message}}, () => {
return;
})
}
else{
this.login(result) //DO SOME OTHER STUFF WITH THE RESULT
}
})
})
}).catch(error => {
console.error("There was an error: " + error);
});
and this worked just fine.
However, I have since restructured my code so that the fetch functionality has been moved into another file. Here's how it looks now (somewhat similar to the tutorial):
fetch.js
let parseResponse = (response) => {
return response.json().then((json) => {
if (!response.ok){
return Promise.reject(json)
}
return json;
});
}
let fetchFunctions = {
post: (url, data) => {
const body = JSON.stringify(data)
fetch(`${apiUrl}${url}`, {
method: 'POST',
headers: headers(),
body: body
})
.then(parseResponse)
}
}
export default fetchFunctions;
Login.jsx
post('/sessions', {person: person})
.then((result) => {
this.login(result) //HERE'S THAT LOGIN FUNCTION I WANT TO RUN
})
Now when I run this, you may not be surprised to learn that I get the error Uncaught TypeError: Cannot read property 'then' of undefined, and I get it, I think... please correct me if I'm wrong, but the reason that this doesn't work is because fetch() is a Promise, but I have now wrapped it inside of a function that is not a Promise.
If I add console.log(json) before the return statement in parseResponse(), I do see my data and it looks good... but how can I get that data out of the Promise and into my component? It seems to me that I need to defined post() as a Promise as well, but I'm not sure how to structure this.
but the reason that this doesn't work is because fetch() is a Promise, but I have now wrapped it inside of a function that is not a Promise.
Functions are not promises. Functions can return promises. You simply forgot to return the result of fetch, which is a promise, from post:
let fetchFunctions = {
post: (url, data) => {
const body = JSON.stringify(data)
return fetch(`${apiUrl}${url}`, {
// ^^^^^^
method: 'POST',
headers: headers(),
body: body
})
.then(parseResponse)
}
}
Now post returns a promises as well.
If you don't return, the implicit return value will be undefined, hence the error message "Uncaught TypeError: Cannot read property 'then' of undefined"
Simplest repro case for this error:
function foo(){}
foo().then();

Why does fetch return a weird hash of integers?

I'm using fetch API with React Native.
My response follows a normal format of {"message": "error here"} if the status is >= 400, which I will show in a native popup.
I'm trying to call response.json() after detecting a failure, but it keeps putting everything in a weird format...
{ _45: 0, _81: 0, _65: null, _54: null }
For whatever reason... the actual response I want is located in _65... I have no idea what these random keys are.
So currently I'm having to access it via _bodyText, but I assume that is wrong because it's a private underscore method.
What am I doing wrong?
var API = (function() {
var base = 'https://example.com/api/v1';
var defaults = {
credentials: 'same-origin',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
};
var alertFailure = function(response) {
if (response.status >= 200 && response.status < 400) {
return response;
} else {
var json = JSON.parse(response._bodyText || '{}');
var message = json.message || 'There was a problem. Close the app, and try again later.';
var error = new Error(message);
error.response = response;
throw error;
}
};
var callAPI = function(url, opts) {
opts.headers['X-Version'] = 'v' + Package.version;
return fetch(base + url, opts)
.then(alertFailure)
.then((response) => {
return response.json();
})
.catch((error) => {
Alert.alert(null, error.message);
});
};
return {
get: function(url, opts) {
var fullOpts = Object.assign({}, defaults, opts);
return callAPI(url, fullOpts);
},
post: function(url, data, opts) {
var fullOpts = Object.assign({}, defaults, {
method: 'POST',
body: JSON.stringify(data || {})
}, opts);
return callAPI(url, fullOpts);
}
};
})();
The answer is that .json() returns a promise... so I had to do everything from within .then()
AsyncStorage.getItems always returns a promise. You can use this method below
AsyncStorage.getItem("access_key").then((value)=>
{
console.log(value);
});
I would recommend you to use the new ES7 syntax async/await, they are easier to understand than using .then()
To use it, just declare some method with the async prefix and inside of it use await to wait for the call to finish.
E.g
async someMethod(){
result = await fetch(URL); // Do some request to any API.
// Use the result after that knowing that you'll have it completed.
}
I hope this helps, at least in my opinion, I find this easier than using .then(), especially when you have to do multiple calls within the same method.

Categories

Resources