Is it possible to fire a .then() block without a return value from a promise? - api

Curious issue I'm having dealing with some callback functions. I need to make a series of API calls that all return promises then I'm trying to take that data and map it to an array that exists on the global scope followed by a function to export the new data as a pdf - my issue is that the then() block is firing before the other function finishes and far before the first API call finshes. `
let fireWatson = async () => {
let watsonClassifed = []
let watsonCallIndex = 0;
let returnedArr = []
for (let i = 0; i < watsonData.length; i++) {
let params = {
classifierId: '***********',
collection: watsonData[i]
}
naturalLanguageClassifier.classifyCollection(params,
function (err, response) {
if (err)
console.log('error:', err);
else
console.log("data returned")
console.log(response.result.collection)
watsonClassifed.push(response.result.collection);
console.log(watsonClassifed)
})
}
}
fireWatson(watsonData).then(res =>
console.log("firing stupid callbback after data")
)
I realize this function isnt actually returning anything but is it possible to still make use of a promise without a return value or is this the main issue im hitting? Ideally - i want the then function to wait until the data is back - mapped to the global array and then outputted but this of course depends on proper synchronicity.
output:
[Done] exited with code=0 in 1.526 seconds
[Running] node "index.js"
firing stupid callbback
data returned
all my sweet sweet data

All functions in JavaScript have returns, it's just that they are implicit if you don't say return explicitly
It's always a bit tricky to mix promises with callbacks. Here is a way you can fireWatson without using any utilities.
let fireWatson = async watsonData => Promise.all(watsonData.map(collection => new Promise((resolve, reject) => {
let params = {
classifierId: '***********',
collection: collection,
}
return naturalLanguageClassifier.classifyCollection(params, function(err, response) => {
if (err) {
reject(err)
} else {
resolve(response)
}
})
})))
Of course, you can simplify this tremendously using a utility I created
const { map } = require('rubico')
let fireWatson = map(collection => new Promise((resolve, reject) => {
let params = {
classifierId: '***********',
collection: watsonData[i]
}
return naturalLanguageClassifier.classifyCollection(params, function(err, response) => {
if (err) {
reject(err)
} else {
resolve(response)
}
})
}))

turns out console.log was firing because every .then() block expects a function.
wrong:
fireWatson(watsonData).then(res =>
console.log("firing stupid callbback after data")
)
right:
fireWatson(watsonData).then(()res =>
console.log("firing stupid callbback after data")
)

Related

Why my function return always _u 0 _v 0 _w null _x null?

How can I return the value from an async function in React Native ?
I am trying to compress images which was given by user.
Here is my compress function where compress each image
const compressFunction = async (item, format = SaveFormat.JPEG) => {
const result = await manipulateAsync(
item.uri,
[],
{ compress: 0, format }
)
return result
};
Here is the function I try to call it. This is not working, console.log return Promise { _u 0, _v 0, _w null, _x null }. compressFunction is working and i can console.log and see the result for each return of "compressFunction(item)" but i can't see them in array list.
const compressImages = (items) => {
const list = items.map( async (item) => ( await compressFunction(item)) )
console.log(list)
}
But for example when i try to call like that it is working.
const compressImages = async (items) => {
const list= await compressFunction(items[0])
console.log(list)
}
I couldn't catch what im doing wrong, I'm working on it for hours. If someone can help me I would be grateful
try await Promise.all(items.map(compressFunction))
Calling list.map(async function) will transform each element in list into a Promise.
Edit: if you make this much more verbose you can see what's going on here
const promises = items.map(item => {
return new Promise((res) => {
compressFunction(item).then(result => {
return res(result);
});
});
});
// Now with the list of promises we have to use the Promise api
// to wait for them all to finish.
Promise.all(promises).then(results => {
// do stuff with results
});
Async/await is just shorthand for this syntax

How to use the functions for list filtering

I am struggling to learn implementing api in vue and filter the data but I can’t make it work.
This is code sandbox I created:
https://codesandbox.io/embed/agitated-architecture-91tj6?file=/src/components/HelloWorld.vue:943-971&codemirror=1
I am able to print the data but not to filter them even though I am using function for it.
If anyone can lead me in right path I would appreciate.
I saw your sandbox, here is how to make your code work:
methods: {
onChange() {
axios
.get("https://api.github.com/users")
.then((response) => {
this.results = response.data;
this.filterResults();
this.modal = true;
})
.catch((error) => {
console.log(error);
this.errors = true;
})
.finally(() => console.log("Data successfully loaded"));
},
filterResults() {
this.filteredResults = this.results.filter((result) => {
return result.login.toLowerCase().includes(this.search.toLowerCase());
});
},
.............
Explanation:
In your code, you write:
onChange() {
axios
.get("https://api.github.com/users")
.then((response) => {
this.filteredResults = response.data;
})
.catch((error) => {
console.log(error);
this.errors = true;
})
.finally(() => console.log("Data successfully loaded"));
this.filterResults();
this.modal = true;
},
You can see the position of this.filterResults(); is wrong. That is because you put the method outside of axios or calling api. The axios is asynchronous function, it means that it will wait for the data to be loaded, while method this.filterResults(); don't need to wait. This means that the method this.filterResults(); will be loaded before the axios function finished even if you put it in the order below the axios function, basically it means that you are calling this.filterResults(); function when the data is still empty. The solution to this is that by calling this.filterResults(); inside the axios function, you can put it in .then() if you want to use the successfully retrieved data.
The second problem in your code is in the filtering logic.
filterResults() {
this.filteredResults = this.results.filter((result) => {
return result.toLowerCase().includes(this.search.toLowerCase()) > -1;
});
},
From what I understand, you want to filter the result with the this.search data from the input. Here you are calling this.results, but when do you assign any value to the this.resuts? in the axios you assign the value to this.filteredResults = response.data; instead of this.results= response.data;, it means the variable this.results is empty. So you have to change this.filteredResults = response.data; to this.results = response.data; in axios.
Next is the logic in your filter
return result.toLowerCase().includes(this.search.toLowerCase()) > -1;
What you want to achieve is filtering the result.login with this.search value. But here you are filtering the result which as object type data with this.search value. Also, you don't need to put > -1 operation, because includes will return true or false.

Error handling in Vue JS - errors not caught

I have the following code:
fooService.update(this.bar).then( this.$emit('updated', this.updatedBar),).catch(err => {...
If an error is encountered, then the error is not caught. If I change the code to be:
fooService.update(this.bar).then(x => {this.$emit('updated', this.updatedBar);}).catch(err => {...
Then the error is caught and shows as expected. Can anyone explain to me what is going on and why it behaves in that way?
Edit
Underlying service code:
function updateBar(bar) {
return $http.put(`/api/bar/${bar.Id}`, bar);
}
So I still think the error is happening in the this.$emit the reason why, in
fooService.update(this.bar).then( this.$emit('updated', this.updatedBar),).catch(err => {
It has to evaluate the this.$emit first as you're setting the response from that function as the .then and not the call itself.
Proof of it doing that
function emit(){
console.log('emit')
}
var promise = new Promise(function(resolve,reject){
setTimeout(() => {
console.log('promise is done')
reject();
}, 1000)
})
promise.then(emit()).catch( function() {console.log('carry on');})
notice how it logs "emit" first
Now if that errors you can see it doesn't hit the catch
function emit(){
console.log('emit')
throw new Error("bad")
}
var promise = new Promise(function(resolve,reject){
setTimeout(() => {
console.log('promise is done')
reject();
}, 1000)
})
promise.then(emit()).catch( function() {console.log('carry on');})
So under the hood it's doing this (the simplest way I can think of)
emit()
try{
getService()
} catch{
...
}
Whereas if you actually pass the .then a function it changes the order of things
function emit(){
console.log('emit')
throw new Error("bad")
}
var promise = new Promise(function(resolve,reject){
setTimeout(() => {
console.log('promise is done')
reject();
}, 1000)
})
promise.then(() => {emit()}).catch( function() {console.log('carry on');})
and again under the hood it looks like this
try{
getService()
emit()
} catch{
...
}

React Native AsyncStorage: using await and async

I'm trying to get all keys from my AsyncStorage database and then filter them in another function, can't seem to get it to wait until AsyncStorage has returned the data?
This function returns the keys in an array:
DATABASE_getAllCoffeeEntries = () => {
AsyncStorage.getAllKeys((err, keys) => {})
.then(keys => {
AsyncStorage.multiGet(keys, (error, items) => {
return items;
}).then(items => {
return items; // array of items is returned
});
});
}
and this function is meant to call the function above then wait for the result then filter down the data.
somefunc = async () => {
var items = await DATABASE_getAllCoffeeEntries();
var someItems = items.filter(function (result, i, item) {
// do filtering stuff
return item;
});
// do something with filtered items
}
Have tried a lot on here but can't get my head around it... any help would be great, thanks.
You need to actually return something from your DATABASE_getAllCoffeeEntries
You could do something like this.
First we wrap your call inside a promise. Which will resolve if we get all the items from AsyncStorage or reject if there is a problem.
Make sure that our calls to AsyncStorage are inside a try/catch. await calls can throw so we need to make sure that we handle any errors.
Use async/await when getting the items from AsyncStorage as this gets ride of the callbacks that are trapping the responses from AsyncStorage. It also can make your code easier to read
Here is the refactor
DATABASE_getAllCoffeeEntries = () => {
return new Promise( async (resolve, reject) => {
try {
let keys = await AsyncStorage.getAllKeys();
let items = await AsyncStorage.multiGet(keys)
resolve(items)
} catch (error) {
reject(new Error('Error getting items from AsyncStorage: ' + error.message))
}
});
}
Then we can call the function like this, though we will have to wrap it in a try/catch as it can throw.
somefunc = async () => {
try {
var items = await this.DATABASE_getAllCoffeeEntries();
var someItems = items.filter(function (result, i, item) {
// do filtering stuff
return item;
});
// do something with filtered items
} catch (error) {
// do something with your error
}
}

how to get multiple fields from expo SecureStore

I am new to ES6 and react-native, trying to get multiple values from the SecureStore.
I think I am misunderstanding promises here ... global.userData is empty in the Promise.all(promises).then function. The relevant values do exist in the secure store
My code is:-
getUserData(fields) {
var promises = [];
var that = this;
global.userData = {};
function getField(field) {
return SecureStore.getItemAsync(field)
.then(res => {
console.log(field+"="+res); // this appears after the log below
global.userData[field] = res;
})
.catch(error => {
global.userData[field] = null;
});
}
fields.map(field => {
promises.push[getField(field)];
});
Promise.all(promises).then(function(v) {
console.log(global.userData); // this is empty
that.setState({ isReady: true }); // allow page to render
});
}
getUserData(["userId", "userName","etc"]);
My bad ... inadvertantly used
promises.push[getField(field)];
should have been:
promises.push(getField(field));
Suprised it wasn't detected as a syntax error ...