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

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()).

Related

synchronous fetch - unexpected results

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
})();

Converting jQuery ajax to fetch

I have this piece of code that calls a function getTableData and expects a Promise in return.
function populateTableRows(url) {
successCallback = () => { ... };
errorCallback = () => { ... };
getTableData(url, successCallback, errorCallback).then(tableData => {
// do stuff with tableData
}
}
This is used in many places across my codebase, and I'm looking to keep the behavior the same as I move away from using jQuery's ajax (and jQuery in general)
In getTableData, I'm currently using $.ajax like so
function getTableData(url, successCallback, errorCallback) {
successCallback = successCallback || function() {};
errorCallback = errorCallback || function() {};
const ajaxOptions = {
type: 'POST',
url: url,
dataType: 'json',
xhrFields: {
withCredentials: true
},
crossDomain: true,
data: { // some data }
};
return $.ajax(ajaxOptions).done(successCallback).fail(errorCallback);
}
This currently returns a Promise for successful requests. For bad requests where fail is invoked, it doesn't appear that a Promise is returned and the then doesn't run in the calling function (which is okay in this case).
When converting the request over to use fetch, I have something like this
function getTableData(url, successCallback, errorCallback) {
successCallback = successCallback || function() {};
errorCallback = errorCallback || function() {};
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
credentials: 'include',
body: { // some data }
})
.then(response => {
let json = response.json();
if (response.status >= 200 && response.status < 300) {
successCallback(json);
return json;
} else {
return json.then(error => {throw error;});
}
}).catch((error) => {
errorCallback(error);
return
});
Successful requests appear to be behaving similarly to the ajax code that I currently have, but now the then callback is running for bad requests which is causing errors in my code.
Is there a way with fetch to mimic the fail behavior of jQuery where the Promise is seemingly aborted for bad requests? I'm fairly new to using Promises and after some experimentation/searching I haven't been able to come up with a solution.
When you .catch() in a chain of promises, it means you already handled the error, and subsequent .then() calls continue successfully.
For example:
apiCall()
.catch((error) => {
console.log(error);
return true; // error handled, returning true here means the promise chain can continue
})
.then(() => {
console.log('still executing if the API call fails');
});
What you want, in your case, is when you handle the error with the callback, to continue to throw it so the promise chain is broken. The chain then further needs a new .catch() block to handle the new error.
apiCall()
.catch((error) => {
console.log(error); // "handled", but we're still not done
throw error; // instead of returning true, we throw the error further
// 👆 this can also be written as `return Promise.reject(error);`
})
.then(() => {
console.log('not executing anymore if the API call fails');
})
.catch((error) => {
// handle the same error we have thrown from the previous catch block
return true; // not throwing anymore, so error is handled
})
.then(() => {
console.log('always executing, since we returned true in the last catch block');
});
By the way, what you return from one then/catch block, the following one will get it as a param.
apiCall()
.then((response) => {
/* do something with response */;
return 1;
})
.catch((error) => { return 'a'; })
.then((x) => console.log(x)) // x is 'a' if there's an error in the API call, or `1` otherwise
In your .catch you implicitly return undefined and thus "handle" the error. The result is a new Promise that fulfills to undefined.
.catch((error) => {
errorCallback(error);
return Promise.reject();
});
should be enough to keep the returned Promise rejecting.
Or you assign the intermediate Promise to a var and return that, and not the result to the fail handling:
var reqPromise = fetch(url, {
// ...
})
.then(response => {
// ...
return json.then(error => {throw error;});
});
reqPromise.catch((error) => {
errorCallback(error);
return
});
return reqPromise;

Caller does not get correct value from async method with ajax

I am trying to wrap my ajax call around a promise. So when the ajax call is complete the promise gets resolved.
In the following snippet:
async function getDetails (data){
let promise = new Promise((resolve, reject) => {
$.ajax({
url: "/get-details",
type:"get",
contentType:"application/json",
data:{"text":data},
success: function(result){
resolve(result.response);
},
error: function(result){
reject(result.response);
}
});
});
let result = await promise;
console.log(`result: ${result}`);
return result;
}
function test() {
let result = getDetails("query-text");
console.log(`result in test : ${result}`);
}
test();
I get correct value in result of getDetails(). The result in test() is a promise object rather than the expected value.
How should I get the desired value from getDetails in an async manner using Promises?
That is because getDetails(...) returns a promise, not the parameter passed into the resolve() or reject() callbacks. Therefore, doing this should work:
async function test() {
let result = await getDetails("query-text");
console.log(`result in test : ${result}`);
}
Alternatively, you can do without an async method by simply waiting for the promise to resolve:
function test() {
let p = getDetails("query-text");
p.then(result => console.log(`result in test : ${result}`));
}
Even beter: using the new fetch() API
Since you're using ES6, you might want to consider using the fetch() API anyway:
async function getDetails (data){
let result = await fetch('/get-details').then(resp => resp.json());
console.log(`result: ${result.response}`);
return result.response;
}
async function test() {
let result = await getDetails("query-text");
console.log(`result in test : ${result}`);
}
test();
When you declare a function as async, it will implicitly return a Promise if not explicitly done so. This means your getDetails function will always return a Promise. Thus, you either need to use a .then callback to get your result from getDetails or use await to "unpack" the result from the promise.
Another thing that you can change in your code is that you do not need to wrap your ajax request in a promise:
jQuery API Documentation:
The jqXHR objects returned by $.ajax() as of jQuery 1.5 implement the
Promise interface, giving them all the properties, methods, and
behavior of a Promise...
This means that your ajax request will resolve and reject for you, and thus allows you to treat the return value like a Promise, allowing you to set your promise variable directly equal to your ajax request:
async function getDetails(data) {
let promise = $.ajax({
url: "/get-details",
type: "get",
contentType: "application/json",
data: {
"text": data
}
});
let result = await promise;
console.log(`result: ${result}`);
return result; // returns a new promise with the return value as the resolve
}
async function test() {
let result = await getDetails("query-text"); // await to "unpack" the promise returned via your async function "getDetails"
console.log(`result in test : ${result}`);
}
test();
But, as #Terry pointed out in his great answer, if you're just using jQuery for sending ajax request, there is really no need (unless you're really worried about browser support). fetch can do the job for you.

Wrap two promise in a function

I have some issue with the return of promise. Before, during an http call, I used a function like this returning one promise:
get_data: function (url)
{
let timestamp = new Date();
return $http({
method: "GET",
url: url
headers: {
'timestamp': timestamp,
}
}).then(
function successCallback(response)
{
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
},
function errorCallback(response)
{
console.dir(response);
return response;
});
},
It was quite straight forward and I could use it like this:
get_data('my_awesome_url').then(function(response){
let my_awesome_data = response
})
The culprit is the timestamp thingy. I use it for some authentification, the why is not important, but by getting it from the client side I was quite often victim of bad horloge or system local set in another langage.
My solution was to create a function that request a server timestamp . But by doing this I must first wait for the timestamp request to hand, then launch another request and... wait for it to end.
This is where I don't really know what to do. My code look like this:
get_data: function (url)
{
let timestamp = new Date();
get_timestamp().then(function(){
return $http({
method: "GET",
url: url
headers: {
'timestamp': timestamp,
}
}).then(
function successCallback(response)
{
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
},
function errorCallback(response)
{
console.dir(response);
return response;
});
});
},
But I'm not sure what I should return. Should I return the get_timestamp promise and in the "then" wait for the other request to end? Should I make the get_timestamp a synchronous call because after all it's just a little date string?
I used the old function all the way accross my code so a way to just keep the old use (with only one then) would be awesome.
As always thanks all.
You would write it that way:
get_data: function(url) {
return get_timestamp() // request the timestamp this returns a promise
.then(function(timestamp) { // on which then is called wich itself returns a promise.
// the callback of this then is called as soon
// as the promise returned by timestamp
// is resolved
return $http({
method: "GET",
url: url
headers: {
'timestamp': timestamp,
}
}) // here you return the Promise that is created by the $http
})
.then(function(response) { // the callback of this then is called as soon
// as the previous promise was resolved
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
})
.catch(function(response) {
console.dir(response);
return response;
});
},
First of all I would use:
.then(function(response) {
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
})
.catch(function(response) {
console.dir(response);
return response;
});
Instead of
.then(
function successCallback(response) {
console.dir("Response:");
console.dir(response["data"]);
return (response["data"])
},
function errorCallback(response) {
console.dir(response);
return response;
});
})
Because it is easier to read later if you have longer chains.
The return returns the last Promise that was created through the chain, the one that was returned by the call .catch(function(response) {...}
You should chain the Promises and return the result of the chain:
function get_data(url) {
return get_timestamp()
.then((timestamp) => {
return $http({
method: "GET",
url: url,
headers: {
timestamp: timestamp
}
});
})
.then((response) => {
console.dir("Response:");
console.dir(response["data"]);
return response["data"];
})
.catch((response) => {
console.dir(response);
return response;
});
}
Note that we only need one .catch at the end of the chain to catch all exceptions.

get callback arguments from sinon.spy inside promise javascript

I'm running a test using mocha and sinon to get a callback value from inside a promise scope of HTTP-request and it doesn't work due to the async nature of promises. It's because by the time sinon.spy checks on callback, It would have been vanished already and become empty or undefined. Here's the testing code:
it('should issue GET /messages ', function() {
server.respondWith('GET', `${apiUrl}/messages?counter=0`, JSON.stringify([]));
let callback = sinon.spy();
Babble.getMessages(0, callback);
server.respond();
sinon.assert.calledWith(callback, []);
});
and the promise:
function requestPoll(props) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(props.method, props.action);
xhr.timeout = 500; // time in milliseconds
if (props.method === 'post' ) {
xhr.setRequestHeader('Content-Type', 'application/json');
}
xhr.addEventListener('load', function(e) {
resolve(e.target.responseText);
});
xhr.send(JSON.stringify(props.data));
});
}
and the call which I'm trying to get callback from on sinon.spy
getMessages: function(counter, callback){
requestPoll({
method: "GET",
action: "http://localhost:9090/messages?counter="+counter
}).then(function(result){
callback(result);
});
}
sinon.spy says it didn't have any arguments (due to async functionality). I tried to look for a way to get result outside the scope and put it on callback.yet I found out it was impossible. also tried resolve and promise return but didn't succeed.
How can I make this unit test pass?
Edit:
this is my attempt:
getMessages: function(counter, callback){
var res;
res = httpRequestAsync("GET",'',"http://localhost:9097/messages?counter=",counter);
console.log(res);
if(res!="")
callback( JSON.parse(res) );
}
I put the request in a separate function:
function httpRequestAsync(method,data,theUrl,counter)
{
return requestPoll({
method: method,
action: theUrl+counter,
data: data
}).then(JSON.parse);
}
It returned res as the promise and inside its prototype there's the promised value I need.
How can I access that promised value over there?
I recommend you not to mix promises and callbacks. If you already have promise based function stick with it
First make getMessages not to break promise chaing. Make it return a Promise
getMessages: function(counter) {
return requestPoll({
method: "GET",
action: "http://localhost:9090/messages?counter=" + counter
}).then(JSON.parse)
}
Then use this promise in your test
it('should issue GET /messages ', function() {
server.respondWith('GET', `${apiUrl}/messages?counter=0`, JSON.stringify([{testdata}]));
const gettingMessages = Babble.getMessages(0);
server.respond();
// return a promise so testing framework knows the test is async
return gettingMessages.then(function(messages) {
// make assertion messages is actually testdata
})
})

Categories

Resources