I am trying to call a function in my action, but get the error this.setToken is not a function:
async function setToken() {
const {
credentials: { year, group, student }
} = this.props;
const fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
firebase
.firestore()
.collection("users")
.doc(fcmToken)
.set({
year
})
.then(function() {
return true;
})
.catch(function(error) {
return false;
});
}
}
export function fetchEvents(id) {
const currentDateString =
moment().format("YYYY-MM-DD") + "T" + "07:00:00.000Z";
const url = xxx;
return async dispatch => {
dispatch(isLoading(true));
const setToken = await setToken(); // call here
fetch(url)
.then(response => {
return response;
})
.then(response => response.json())
.then(data => {
const { error } = data;
if (error) {
dispatch(hasErrored(error.message));
} else {
dispatch(fetchSuccessEvents(data.items));
}
navigate("Month");
dispatch(isLoading(false));
});
};
}
Any ideas?
setToken is defined as a standalone function, not a property of an instance, or of the current this, or anything like that:
async function setToken() {
So, call it like you would call any other function, without putting this in front of it.
You also cannot use the same variable name in the inner scope, else you'll refer to the inner variable (which will start out as undefined / uninitialized); assign the result to a different variable name instead:
const token = await setToken();
But your current code doesn't have setToken resolve to anything, in which case, you may simply await it, without assigning the result to anything:
await setToken();
Use
setToken()
instead of
this.setToken()
You are not in a class environment, as far as I can tell, so setToken() is not an instance or a property, but a standalone function.
If you prefer to use a class notation, you could use window.setToken().
Related
I know this sounds rediculess...
In my function index() I retrieve a Model for a specific collectionName promted by commandline parameters. After a async AXIOS API call a saveToDB(response, model) is called.
export async function index(collectionName) {
let model = getModelForCollection(collectionName)
await axios.get(url)
.then(resp => {
saveToDB(resp.data, model)
.then(saved => {
...
})
})
}
async function saveToDB(response, model) {
const word = new model({response})
await word.save()
.then(() => {
console.log("saved")
})
}
So basicly this should easily work, but the strange thing is. This does only! work, if I save another document prior to calling saveToDB() in index(). Changing the function to;
export async function index(collectionName) {
let model = getModelForCollection(collectionName)
let word = new model({word: 'WORT'})
await word.save().then(() => {
console.log("saved")
})
await axios.get(url)
.then(resp => {
saveToDB(resp.data, model)
.then(saved => {
...
})
})
}
Is there anything strange going on I am not aware of? Excuse my super unspecific question.
Maybe you should read the docu: https://mongoosejs.com/
or search for related questions. I guess its not a mongoos problem.
Why are you consuming and "awaiting" the Promises at the same time. Why don't you keep it simple like this:
export async function index(collectionName) {
try {
let model = getModelForCollection(collectionName)
let resp = await axios.get(url);
let saved = await saveToDB(resp.data, model);
} catch (error) {
console.log("Error");
console.log(error);
}
}
async function saveToDB(response, model) {
try {
const word = new model({ response });
let writeStatus = await word.save();
console.log("saved");
} catch (error) {
console.log("Error");
console.log(error);
}
}
I have a function that is triggered when I click a button. Based on the docId I can retrieve the right download URL in Firestore. But I also want to update a specific field (availableDownloads) in my "customers" collection. I can't get it to work.
This code works fine. It returns the download url.
exports.getDownloadUrl = functions.https.onCall(async(data, context) => {
var docRef= await db.collection('projects').doc(data.docId);
return docRef.get().then(function(doc){
const downloadURL = doc.data().downloadURL;
return downloadURL;
}).catch(function(error) {
// Handle error
});
});
However this doesn't. It returns null
exports.getDownloadUrl = functions.https.onCall(async(data, context) => {
var docRef= await db.collection('projects').doc(data.docId);
return docRef.get()
.then(async function(doc){
const downloadURL = doc.data().downloadURL;
const userRef = await db.collection('customers').doc(context.auth.uid);
return userRef.update({
availableDownloads: admin.firestore.FieldValue.increment(-1)
}).then(()=> {
return downloadURL;
}).catch((error)=> {
})
}).catch(function(error) {
// Handle error
});
});
The first thing that jumps out at me is that .update() is an asynchronous function so you need an await in front of userRef.update(...).
You should also consider sticking to either arrow functions (() => {}) or anonymous functions (function () {}), but that's just a stylistic note. :)
ETA: as Frank van Puffelen pointed out, you don't need awaits for simply declaring a DocumentReference since those are not async functions. (looking at the line var docRef= await db.collection('projects').doc(data.docId);).
In addition to the potential problems due to the mix of async/await with then/catch, your main problem is due to variable scoping. The downloadURL variable is a local variable declared inside the first then() block and is therefore not accessible in the second then() block.
You can easily see the problem with the following code:
function later(delay, value) {
return new Promise((resolve) => setTimeout(resolve, delay, value));
}
later(500, 'value1')
.then((value) => {
console.log('First then block: ' + value);
const downloadURL = value;
return later(1000, 'value2');
})
.then((value) => {
console.log('Second then block: ' + value);
console.log('Second then block: ' + downloadURL); // Here it is going to throw an error
})
.catch((error) => {
console.log(error);
});
You need to declare the downloadURL variable in the scope of the Cloud Function, as follows:
exports.getDownloadUrl = functions.https.onCall((data, context) => {
let downloadURL;
const docRef = db.collection('projects').doc(data.docId);
return docRef.get()
.then((doc) => {
downloadURL = doc.data().downloadURL;
const userRef = db.collection('customers').doc(context.auth.uid);
return userRef.update({
availableDownloads: admin.firestore.FieldValue.increment(-1)
});
})
.then(() => {
return downloadURL;
}).catch((error) => {
// Handle error
})
});
Note that the above code does not use async/await. If you want to use async/await, do as follows:
exports.getDownloadUrl = functions.https.onCall(async (data, context) => {
try {
const docRef = db.collection('projects').doc(data.docId);
const doc = await docRef.get();
const downloadURL = doc.data().downloadURL;
const userRef = db.collection('customers').doc(context.auth.uid);
await userRef.update({
availableDownloads: admin.firestore.FieldValue.increment(-1)
})
return downloadURL;
} catch (error) {
// Handle error
}
});
You can see that with this code, due to the fact that it is simplified by using async/await, there is no more complexity due to variable scoping.
I'm trying to send a request to my Django API from an Ionic4-Angular front-end, the problem is that I've not become familiar with this tecnology or even javascript yet.
In the following code I'm trying to return the subscribe data and assign it to a const.
async getUserLogged() {
const tkn = await this.authService.getToken();
const user = await this.http
.post('http://localhost:8000/api/getuser/', {
token: tkn
})
.subscribe(response => {
console.log(response);
return response;
});
console.log(user);
return user;
}
The first console.log contains the real object i'm searching for.
However, the second one prints a Subscriber object.
Can anyone explain me this behaviour and how can I fix it
Here is how I would set up this method:
getUserLogged() {
return this.authService.getToken().pipe(
switchMap(tkn=> {
return this.http.post('http://localhost:8000/api/getuser/', {
token: tkn
})
})
);
}
You then use this method as follows:
getUserLogged().subscribe(userData => console.log(userData));
This approach uses the switchMap operator that only calls http.post once authService.getToken returns the token.You can find the documentation to all RxJs operators here.
callback function of subscribe cannot return any value to user. it only gets Subscriber object as you mentioned which is result of observable.
If you want to use async, await approach, you need to use toPromise method to make an observable a promise.
async getUserLogged() {
const tkn = await this.authService.getToken();
const user = await this.http
.post('http://localhost:8000/api/getuser/', {
token: tkn
}).toPromise();
console.log(user);
return user;
}
Converting your observable response to a promise, makes it easy to call from where you need.
const getUserLogged = () => {
const tkn = await this.authService.getToken();
return this.http
.post('http://localhost:8000/api/getuser/', {
token: tkn
})
.toPromise();
};
const someOtherFunc = async () => {
const user = await getUserLogged();
console.log({ user });
};
I have trouble to get this to work.
I have a function getItem:
export const getItem = async (key, callback) => {
value = await Expo.SecureStore.getItemAsync(key).catch((error) => console.log(error));
callback(value);
}
getItem is supposd to get a token and pass that token to the callback.
Now I want to use getItem in this (simplified) class:
export class Post {
constructor(){
this.token = false;
}
post() {
console.log('Token: ' + this.token);
...
}
setToken(token){
console.log('Set token.');
this.token = token;
}
authorizedPost() {
getItem('token', this.setToken.bind(this)).then(
this.post()
);
}
}
I use this class like this:
let post = new Post();
post.authorizedPost();
This is the output I get:
Token: false
Set token.
But I need the token to be set and after that, I want to call the method this.post();
Since I'm a beginner, I want to excuse if that issue is trivial. But I am thankful for every help!
I had to pass a function to then like this:
authorizedPost() {
getItem('token', this.setToken.bind(this)).then(
()=>this.post()
);
}
I know you didn't really ask, but…
Part of the reason the code is difficult to debug is because you are using callbacks when you don't need to and mixing them with promises. These are two different async paradigms that are normally better off separate. You can get rid of the callback in your code in a way that will make it much more readable.
Expo.SecureStore.getItemAsync()
returns a promise, so just return it.
const getItem = (key) => Expo.SecureStore.getItemAsync(key);
Then in your method you can call then and simply call the function you were passing as a callback. No need for callback or bind. Just one line after the other:
authorizedPost() {
getItem('token').then(val => {
this.setToken(val) // the order is now obvious
this.post()
})
.catch((error) => console.log(error))
}
Here's a snippet with a faked Expo method:
let Expo = {
SecureStore: {
getItemAsync() {
return new Promise((resolve, reject) => setTimeout(() => resolve("someFancyToken"), 1000))
}
}
}
class Post {
constructor() {
this.token = false;
}
post() {
console.log('Token: ' + this.token);
}
setToken(token) {
console.log('Set token.');
this.token = token;
}
authorizedPost() {
getItem('token').then(val => {
this.setToken(val)
this.post()
})
.catch((error) => console.log(error))
}
}
const getItem = (key) => Expo.SecureStore.getItemAsync(key);
let post = new Post();
post.authorizedPost();
I am trying to understand async calls using async/await and try/catch.
In the example below, how can I save my successful response to a variable that can be utilized throughout the rest of the code?
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample();
console.log(globalData) //Promise { <pending> }
1) Return something from your asyncExample function
const asyncExample = async () => {
const result = await axios(users)
return result
}
2) Call that function and handle its returned Promise:
;(async () => {
const users = await asyncExample()
console.log(users)
})()
Here's why should you handle it like this:
You can't do top-level await (there's a proposal for it though);
await must exist within an async function.
However I must point out that your original example doesn't need async/await
at all; Since axios already returns a Promise you can simply do:
const asyncExample = () => {
return axios(users)
}
const users = await asyncExample()
try..catch creates a new block scope. Use let to define data before try..catch instead of const, return data from asyncExample function call
(async() => {
const users = 123;
const asyncExample = async() => {
let data;
try {
data = await Promise.resolve(users);
} catch (err) {
console.log(err);
}
return data;
};
//Save response on a variable
const globalData = await asyncExample();
console.log(globalData);
// return globalData;
})();
I had same issue with you and found this post. After 2 days of trying I finally found a simple solution.
According to the document of JS, an async function will only return a Promise object instead of value. To access the response of Promise, you have to use .then()method or await which can return the resulting object of Promise is instead of Promise itself.
To change variables from await, you have access and change the variable you want to assign within the async function instead of return from it.
//Save response on a variable
var globalData;
const asyncExample = async () =>{
try {
const data = await axios(users);
globalData = data; // this will change globalData
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
asyncExample();
But if you do this, you may get an undefined output.
asyncExample();
console.log(globalData) //undefined
Since asyncExample() is an async function, when console.log is called, asyncExample() has not finished yet, so globalData is still not assigned. The following code will call console.log after asyncExample() was done.
const show = async () => {
await asyncExample();
console.log(globalData);
}
show();
Because the events are happening asynchronously you need to tie in a callback/promise. I'm going to assume it returns a promise.
const axios = require('axios');
const users = 'http://localhost:3000/users';
const asyncExample = async () =>{
try {
const data = await axios(users);
console.log(data); //200
}
catch (err) {
console.log(err);
}
};
//Save response on a variable
const globalData = asyncExample().then( (success, err) => {
if (err) { console.error(err); }
console.log(success)
}
Just use a callback/promise (cascading programming):
axios(users).then(function(response) {
const globalData = response;
console.log(globalData)
});