How to run a function with a delay from inside axios then - vuejs2

I'm, a bit new to vue so please be kind. I have a simple push call I want to run 2 seconds after a user uploads a photo to my app. I'm trying to do this inside my axios "then" but it happens instantly. I want to make this take place after 2 seconds. How can I use setTimeout to accomplish this?
I've seen the answer here...How to add delay to promise inside then
But my mind is melting trying to understand it.
```
axios.put(`${process.env.KITTY_URL}/api/v1/cats/${this.singleCat.id}/`,formData,{
onUploadProgress: progressEvent => {
console.log('Upload progress: ' + Math.round(progressEvent.loaded / progressEvent.total * 100) + '%')
}
})
.then(response => {
response.status === 200 ? this.showSuccess = true : this.showDanger = true;
this.singleCat = response.data;
// NOTE: set a timer to run this line 2 seconds after success response
setTimeout(function() { this.$router.push('/login'); }, 2000)<---WHAT SHOULD THIS LOOK LIKE?
})
.catch(error => {
console.log(error);
this.showDanger = true;
})
```

First off, you need to bind the context. Easiest way to do this is just with an arrow function. So you would just need to change that line to this:
setTimeout( () => this.$router.push('/login'), 2000);
Another way is to use async/await:
let response = await axios.put(...);
setTimeout( () => this.$router.push('/login'), 2000);
Though you won't have as nice of error handling this way: https://github.com/axios/axios/issues/1086
Similar question: Accessing VUE JS's data from Axios

Related

Cypress spy on multiple calls of the same method

I'm trying to check that a method was not called again after a certain action.
My test:
it('if query is less than 3 symbols, api call is not made', () => {
cy.spy(foo, 'bar').as('bar');
cy.get('input').type('12').then(() => {
cy.get('#bar').its('callCount').then(res => {
expect(res).to.eq(1); // a basic check after mounted hook
});
});
});
My component:
async mounted(): Promise<void> {
await this.foo.bar();
}
async getSearchResults(): Promise<void> {
if (this.searchQuery.length < 3) {
return;
}
await this.foo.bar();
}
The problem is that bar was already called on mount, and it could have been called multiple times before, if query length was valid. I was thinking about saving bar's callCount to a variable and checking it after call, but that looks ugly. Kinda stuck here, any ideas are welcome.
It's not an issue. The call count is started at the point you set up the spy, not when the component is mounted.
Try this:
const foo = {
bar: () => console.log('bar called')
}
it('starts with a clean callcount', () => {
foo.bar() // make a call
cy.spy(foo, 'bar').as('bar'); // callCount === 0 on setup
cy.get('#bar')
.its('callCount')
.should('eq', 0) // passes
});
Even if you have some callcount from another test, you can always reset it before the current test:
it('allows reset of spy callCount', () => {
cy.spy(foo, 'bar').as('bar'); // callCount === 0 on setup
foo.bar() // make a call, count is now 1
cy.get('#bar').invoke('resetHistory') // remove prior calls
cy.get('#bar')
.its('callCount')
.should('eq', 0) // passes
});
I believe you can get the initial call count, and then wrap your test in that.
it('if query is less than 3 symbols, api call is not made', () => {
cy.spy(foo, 'bar').as('bar');
cy.get('#bar').its('callCount').then((initRes) => {
cy.get('input').type('12').then(() => {
cy.get('#bar').its('callCount').then(res => {
expect(res).to.eq(initRes); // a basic check after mounted hook
});
});
});
});
You would probably want to do a test that this would fail, to make sure that Cypress is getting '#bar' again.

Use response from first axios in second axios call

i wrote an application in VueJS and i have to send first a get call to get the redirect url.
this.$axios.get('http://first-url.call').then(function(response) {
console.log(response.request.res.responseUrl)
let postUrl = response.request.res.responseUrl
}).catch(function(error){
console.log(error)
});
In my next call i want to use the "response.request.res.responseUrl" as post url
this.$axios.post(postUrl).then(function(response) {
console.log(response);
}).catch(function(error){
console.log(error)
});
Unfortunately, i cannot save the "response.request.res.responseUrl" response in a js variable. I'm not so familiar with async / await so maybe someone can help how i can store the first response into a value that i can use in my second call?
It's simpler to write it exclusively with async..await syntax:
try {
let response = await this.$axios.get('http://first-url.call')
let postUrl = response.request.res.responseUrl;
response = await this.$axios.post(postUrl)
...
} catch (err) {
...
}
It's beneficial to know the exact meaning of it. The key is correct promise chaining:
this.$axios.get('http://first-url.call')
.then((response) => {
let postUrl = response.request.res.responseUrl;
return this.$axios.post(postUrl)
})
.then((response) => {...})
.catch(...);
This way promises are executed in correct order and don't have unnecessary nested callbacks.
Be aware of incorrect use of function, it will lead to this problem.
Here is a solution without async/await: Make the second axios call within the first successful call. You need to use arrow functions for this so you keep the correct this on your Vue instance:
this.$axios.get('http://first-url.call').then((response) => {
console.log(response.request.res.responseUrl)
let postUrl = response.request.res.responseUrl
this.$axios.post(postUrl).then((response) => {
console.log(response);
}).catch(function(error){
console.log(error)
});
}).catch((error) =>{
console.log(error)
});
There are a number of ways to deal with this.
The simplest is to create a variable outside the callbacks. Though the following still relies on your first call completing before the second one.
let postUrl = null
this.$axios.get('http://first-url.call').then(function(response) {
postUrl = response.request.res.responseUrl
})
The next best (really the best) solution is to use async/await instead of .then() so you don't have callback scoping issues. This approach also guarantees you won't have a race condition because it will "await" for the first call to finish before making the second call.
The following would need to take place inside a function with async.
async function doWhatever() {
const response = await this.$axios.get('http://first-url.call')
const postResponse = await
this.$axios.post(response.request.res.responseUrl)
}
Finally, the last option is you nest your callbacks. This allows the second callback to have access to the scope of the first callback.
this.$axios.get('http://first-url.call').then(function(response) {
this.$axios.post(response.request.res.responseUrl).then(function(postResponse) {
// do something with the postResponse
})
})
Also, while the above code will work, it's usually better to chain promises.
this.$axios.get('...')
.then(function(response) {
// we return the promise so it can be handled in the next .then()
return this.$axios.post(response.request.res.responseUrl)
})
.then(function(postResponse) {
// do something with the postResponse
})
Notice how this last example starts to look a lot like async/await.

React Native Doesn`t re-render after context state change

state changes after getting data from api. i can see that with console.log()
but doesn`t render data after update. there is my code fetch code :
useEffect(() => {
axios
.get(
'https://mylink.ngrok.io/channels/getUser',
).then((data)=>{
let array = [];
const promises = data.data.channels.map((channel)=>{
axios.get(`https://mylink.ngrok.io/channels/getChannel/${channel}`).then((resp)=>{
array.push(resp.data);
})
})
Promise.all(promises).then(()=>{
setonLineChannels(array);
});
})
}, []);
btw its in context.
I think Promise.all casue of that
thx
Where you declare promises, it's not going to wait for those API requests to complete. They're running even before you've called Promise.all(promises).
You can just put the code you've defined as promises inside the Promise.all() directly, then make sure you return the get request inside. And don't push to array. Your map will return an array of promises, and you can use the results array in the .then after Promise.all to update your state directly.
Here's what I mean:
useEffect(() => {
axios.get("https://mylink.ngrok.io/channels/getUser").then((data) => {
Promise.all(
data.data.channels.map(
(channel) =>
axios
.get(`https://mylink.ngrok.io/channels/getChannel/${channel}`)
.catch((err) => console.error(err)) // this allows an individual request to fail
)
).then((results) => setonLineChannels(results));
});
}, []);

React Native listen to variable changes

I am trying to make an app that will allow me to update a textbox after receiving a change in a variable. However the variable takes a long time to update, and await does not work to wait for my variable to update probably because of the timeout function I used. How do I create a listener or something of that sort to check for any variable changes?
Below is my code snippet
const [banana, setBanana] = useState(-1);
const updateLocation = async() => {
const majorSig = scheduledScan();
setBanana(majorSig);
}
const scheduledScan = async() => {
beaconScan();
// Scans for 20 seconds
setTimeout( async()=> {
beaconStop();
await getAndUpdateUserLoc();
// console.log("Loggged: ", await getUserLoc());
// console.log("major: ", await getSignificantMajor());
currentMajor = await getSignificantMajor();
return currentMajor;
}, 20000);
}
When I run updateLocation(), my code is supposed to run for 20 second. I want it to wait until it finishes running scheduledScan() and returns a value to majorSig before it runs the setState function. However right now all it does is run scheduledScan() and update setState immediately to a wrong value. What should I do to make it behave in the way I want?
Thank you.
Firstly, in your async updateLocation function, your await statement is missing. Let's add it appropriately:
const updateLocation = async () => {
const majorSig = await scheduledScan();
setBanana(majorSig);
};
Then, It would be a good idea if you follow a promise approach in your time-limited function by using a Promise.race which lets your function either time out or successfully return a value:
const scheduledScan = async () => {
beaconScan();
return Promise.race([
async () => {
beaconStop();
await getAndUpdateUserLoc();
// console.log("Loggged: ", await getUserLoc());
// console.log("major: ", await getSignificantMajor());
currentMajor = await getSignificantMajor();
return currentMajor;
},
new Promise((_, reject) => setTimeout(() => reject(new Error('Request timed out')), 20000)),
]);
};

Intercept Promise error with vue-resource

I'd like to intercept all api responses with the code != 200 in my main.js with the following code, dispatch an action and after that, show a toast showing the error message. I'm using vue-resource and my interceptor is the following:
Vue.http.interceptors.push(function(request, next) {
next(function(response) {
debugger;
if (response.status != 200) {
store.dispatch("errorAction", response);
}
});
});
But the code inside the callback is never reached...
And my api call is done this way. The java controller just throws an exception with the 500 error code.
Vue.http
.get(`http://${ADDRESS}/${store.state.module}/foo/exception`)
.then(() => {}, () => {});
I'm new with Promises and probably i'm messing things, but i don't want to be passing an error callback to every single promise. And what if my request is as follows:
export function getFoo(cb) {
Vue.http
.get(`http://${ADDRESS}/${store.state.module}/foo`)
.then(
response => {
return response.json();
},
() => {}
)
.then(foos => {
cb(foos);
});
}
I would like to get rid off () => {} and use the interceptor code to be run.
I think I'd had the same issue, so I tried to look at the documentation. Well, it is very simple, you just need to do something like this:
In main.js
Vue.http.interceptors.push(function(req) {
//Here you can add some headers, if needed
req.headers.set('awesomeHeader', 'owwnt')
return function(res) {
if( res.status == 200 || res.status == 201 || res.status == 202 ){ //Here you add the status codes that you'll work with, just like my example
//Sucess response
} else {
//Every time witch an request return a status differ form the list above, you can do whatever you want, for example you can redirect the page for a new one
window.location.href = `http://localhost:8080/#`
//If you want to display a notification, you need to import the component before, and then do something like it:
Notification.success({
title: 'error',
message: 'Unauthorized request!',
offset: 100
})
}
};
})
Does it answer you question? I hope it does.