JavaScript function(callback).then(), callback executes after then - javascript

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

Related

How to use async await with subscribe angular

I'm calling createOtherKind() function and trying to use the value of this.listKinds. My problem is that isKindOtherCreated is not waiting this.getKinds() to finish and this.listKinds is undefined.
How can i do it?
Code:
getKinds(): void {
this.detailsService.getKinds().subscribe(async response =>{
this.listKinds = await response.body;
})
}
async createOtherKind() {
await this.getKinds();
const isKindOtherCreated = this.listKinds.find(kind => kind.name === "Other");
if(!isKindOtherCreated) {
this.createdKind.name = "Other";
this.createKind();
}
}
You can call the createOtherKind() method inside getKinds() method. This is how you will get the value from this.listKinds variable. subscribe is async method. So, it does not wait for the reponse.
Here is Example:
getKinds(): void {
this.detailsService.getKinds().subscribe(async response =>{
this.listKinds = response.body;
await this.createOtherKind();
})
}
async createOtherKind() {
const isKindOtherCreated = this.listKinds.find(kind => kind.name === "Other");
if(!isKindOtherCreated) {
this.createdKind.name = "Other";
this.createKind();
}
}
The issue is happening because you're mixing Observables (where you have subscribe) with async/await. If you want to stick to using promises you could revise your getKinds function to something like this:
getKinds(): void {
// Convert your observable to a Promise so you can use `await` on it
return this.detailsService.getKinds().toPromise().then((response) => {
this.listKinds = await response.body;
});
}
For more details, I recommend taking a look at this similar question.
You use subscribe here, so I guess this.detailsService.getKinds() returns an observable. You can make things with it then return it to be able to subscribe something else in another part of your code. like:
getKinds(): Observable<any> {
let myObs = this.detailsService.getKinds(); //Getting your observable
//Reading result
myObs.subscribe(response => {
this.listKinds = response.body;
});
//Returning it to use it somewhere else
return myObs;
}
createOtherKind() {
//Make request and subscribe to observable
this.getKinds().subscribe( res => {
const isKindOtherCreated = this.listKinds.find(kind => kind.name === "Other");
if(!isKindOtherCreated) {
this.createdKind.name = "Other";
this.createKind();
}
}
}
EDIT
As said by Andres2142, subscribe twice isn't exactly the right way to act. It should work but the result isn't perfectly stable, the best way is to replace the first subscribe by a tap pipe since it is desinged to perform side-effects. So a better answer is:
getKinds(): Observable<any> {
return this.detailsService.getKinds()
.pipe(
tap( (response) => {
this.listKinds = response.body;
})
);
}
// createOtherKind() Doesn't need to change

Mongoose Object.save() only working if called twice

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

Not able to call http post method

I am new to javascript world. httpService.post is never executed. How can I achieve this?
async createInboundRequest_1(payload: InboundRequestDto) {
console.log("Request Dto ");
console.log(payload);
return this.fetchLookupReferenceData(payload).then(res => {
console.log("Final Response ");
console.log(res);
this.httpService.post(this.api_url, res).pipe(
map(res_1 => res_1.data),
)
});
}
One of my colleagues suggested to change the above code as given below and it worked but I am still not sure why this works.
async createInboundRequest_1(payload: InboundRequestDto) {
const enrichedRequest = await this.fetchLookupReferenceData(payload);
const res = this.httpService.post(this.api_url, enrichedRequest).subscribe(res_1 => {
return res_1.data;
})
}

this.setToken is not a function

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

Using the MobX #action decorator with async functions and .then

I'm using MobX 2.2.2 to try to mutate state inside an async action. I have MobX's useStrict set to true.
#action someAsyncFunction(args) {
fetch(`http://localhost:8080/some_url`, {
method: 'POST',
body: {
args
}
})
.then(res => res.json())
.then(json => this.someStateProperty = json)
.catch(error => {
throw new Error(error)
});
}
I get:
Error: Error: [mobx] Invariant failed: It is not allowed to create or change state outside an `action` when MobX is in strict mode. Wrap the current method in `action` if this state change is intended
Do I need to supply the #action decorator to the second .then statement? Any help would be appreciated.
Do I need to supply the #action decorator to the second .then statement? Any help would be appreciated.
This is pretty close to the actual solution.
.then(json => this.someStateProperty = json)
should be
.then(action(json => this.someStateProperty = json))
Keep in mind action can be called in many ways that aren't exclusive to #action. From the docs on action:
action(fn)
action(name, fn)
#action classMethod
#action(name) classMethod
#action boundClassMethod = (args) => { body }
#action(name) boundClassMethod = (args) => { body }
are all valid ways to mark a function as an action.
Here's a bin demonstrating the solution: http://jsbin.com/peyayiwowu/1/edit?js,output
mobx.useStrict(true);
const x = mobx.observable(1);
// Do async stuff
function asyncStuff() {
fetch('http://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
// .then((objects) => x.set(objects[0])) BREAKS
.then(mobx.action((objects) => x.set(objects[0])))
}
asyncStuff()
As for why your error actually happens I'm guessing that the top level #action doesn't recursively decorate any functions as actions inside the function it's decorating, meaning your anonymous function passed into your promise wasn't really an action.
To complement the above answer; indeed, action only works on the function you pass to it. The functions in the then are run on a separate stack and should therefor be recognizable as separate actions.
Note that you can also give the actions a name as well so that you easily recognize them in the devtools if you use those:
then(action("update objects after fetch", json => this.someStateProperty = json))
note that in async method you manualy have to start a new action/transaction after awaiting something:
#mobx.action async someAsyncFunction(args) {
this.loading = true;
var result = await fetch(`http://localhost:8080/some_url`, {
method: 'POST',
body: {
args
}
});
var json = await result.json();
#mobx.runInAction(()=> {
this.someStateProperty = json
this.loading = false;
});
}
my preference
I prefer to not use #mobx.action/runInAction directly but always place it on an private method. And let public methods call private methods that actually update the state:
public someAsyncFunction(args) {
this.startLoading();
return fetch(`http://localhost:8080/some_url`, {
method: 'POST',
body: {
args
}
})
.then(res => res.json())
.then(this.onFetchResult);
}
#mobx.action
private startLoading = () => {
this.loading = true;
}
#mobx.action
private onFetchResult = (json) => {
this.someStateProperty = json;
this.loading = false;
}

Categories

Resources