I am trying to store the result of an async function to a const but with no success.
The code:
Here is working ok and return the converted json
import pako from 'pako';
export const unzipFile = async (json_to_unzip) => {
try {
// fetch file with CORS enabled
const res = await fetch(json_to_unzip, {
mode: 'cors',
});
// convert to arrayBuffer for further processing
const buf = await res.arrayBuffer();
// and convert blob to arrayBuffer using `await blob.arrayBuffer()`
console.log('input size: ', buf.byteLength);
// decompress file
const outBuf = pako.inflate(buf);
console.log('output size: ', outBuf.byteLength);
// convert arrayBuffer to string
const str = new TextDecoder().decode(outBuf);
// print json object
const unzippedFile = JSON.parse(str);
console.log('json object', unzippedFile[0]);
return unzippedFile[0];
} catch (err) {
console.error('unable to decompress', err);
}
};
From here I want to store it to a const:
const emissions = (async () => {
const res = await unzipFile(emissions_json);
return res;
})();
const emissions = unzipFile(emissions_json).then(res => res)
console.log(emissions) still returns a pending promise in both cases
Async functions always return a promise. If the return value of an async function is not explicitly a promise, it will be implicitly wrapped in a promise.
Docs: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
emissions is a promise because it holds the promise object returned from the async anonymous function. To console.log the result you can do:
emissions.then(res => console.log(res))
This happens because you are trying to manage the result of an asynchronous operation so you can not treat it as if it was a synchronous operation. With that said, to store the result to a const you need to do it inside a proper async function as you did with the await keywoard or by using a Promise.then() handler:
unzipFile(emissions_json).then(res => {
//logic for using the res
})
Related
I am querying some data from IPFS using axios, the problem is that after calling the specific api the return value is a promisse from axios.
const getNFTDetail = async (url: string) => {
const urlIPF = url.replace("ipfs://", "https://cloudflare-ipfs.com/ipfs/");
try {
return await axios.get(urlIPF).then((res) => {
return res.data;
});
} catch (error) {
console.log(error);
}
};
response I get:
is there a way to wait until promisse has been resolved?, as you see I am already using async await on the function call.
just, decide if you use async / await or .then / .catch:
const getNFTDetail = async (url: any) => {
const urlIPF = url.replace("ipfs://", "https://cloudflare-ipfs.com/ipfs/");
const { data } = await axios.get(urlIPF);
return data;
};
or
const getNFTDetail = (url: any) => {
const urlIPF = url.replace("ipfs://", "https://cloudflare-ipfs.com/ipfs/");
axios.get(urlIPF).then(({data}) => {
// use your data here
}).catch((err)=>{
console.log(err);
};
};
When you fetch a request, no matter any http client you use, will then return a Promise.
Just use await to get a response from your request.
const response = await axios.get(your-url);
const json = await response.json();
To use typescript correctly, type the url a string: (url: string) instead of happy any type.
I am fetching data from the blockchain. Contract adresses in this case.
Once I have the adresses I fetch some info on each specific adresses and add a key=>value pair to the object. This is all working and I'm getting all the right data. However, once in the component, the newly added key=>value pair is no longer present. I think this is because the value added is a promised and the dispatch is not waiting on it. How can I fix this so the dispatch it done only once the promised is resolved.
const tokenStream = await exchange.getPastEvents('OtherToken', {fromBlock:0, toBlock: 'latest'})
const allTokens = tokenStream.map((event) => event.returnValues)
console.log('ALL TOKEN DATA : ', allTokens)
allTokens.forEach( async element => {
let symbol = await exchange.methods.getERCsymbol(element.tokenAddress).call()
element.symbol = symbol
});
console.log('ALL TOKEN DATA AFTER : ',allTokens) // I see symbol
dispatch(allTokensLoaded(allTokens))
Better solution would be to use Promise.all to wait for multiple async request/promise to finish, and also you are mixing await and then, as your main function is already async you can write it in more neat and clean way using await only.
export const loadAllTokens = async (exchange, dispatch) => {
const result = await exchange.getPastEvents('OtherToken', {fromBlock:0, toBlock: 'latest'});
const allTokens = result.map((event) => event.returnValues);
await Promise.all(allTokens.map(async (element) => {
const innerResult = await exchange.methods.getERCsymbol(element.tokenAddress).call();
element.symbol = innerResult;
element[2]= innerResult;
}));
dispatch(allTokensLoaded(allTokens));
}
its more clean and better to understand :).
if any doubts please comment.
They happens to be nested async request and this is how I fixed it
export const loadAllTokens = async (exchange, dispatch) => {
await exchange.getPastEvents('OtherToken', {fromBlock:0, toBlock: 'latest'}).then( async(result) => {
const allTokens = result.map((event) => event.returnValues)
let count = 0
allTokens.forEach( async element => {
await exchange.methods.getERCsymbol(element.tokenAddress).call().then( async(result) =>{
element.symbol = result
element[2]= result
count += 1
}).then( () => {
if(count === allTokens.length){
dispatch(allTokensLoaded(allTokens))
}
})
})
})
}
Understanding: my little understanding with promise is we can use it in place of callback functions.
Scenario: i will fetch parent categories from api than pass the categories array of objects to a function, a promise function may be ?
const getData = async () => {
const res = await fetch('API/categories?parent=0');
const data = await res.json();
return Promise.all(data.map(item => anAsyncFunction(item)))
}
this function recieves all the categories and i pass it to promise function
const anAsyncFunction = async item => {
return functionWithPromise(item)
}
this returns right data all child categories in array of objects
const functionWithPromise = async data => { //a function that returns a promise
const res = await fetch('API/categories?parent='+data.id);
const datas = await res.json();
// console.log(datas);
// api call to insert all the records
return Promise.resolve(data)
}
now i want to go through all these arrays and insert into database using api call
await api.create(postData)
You can merge anAsyncFunction & functionWithPromise functions. Two functions are unnecessary.
The line return Promise.resolve(data) can be return data
getData is thenable which gives you array of promise responses. Loop thru it twice and use Promise.all
const getData = async () => {
const res = await fetch("API/categories?parent=0");
const data = await res.json();
return Promise.all(data.map((item) => anAsyncFunction(item)));
};
const anAsyncFunction = async (data) => {
const res = await fetch("API/categories?parent=" + data.id);
const datas = await res.json();
// api call to insert all the records
// return Promise.resolve(data);
return datas;
};
getData().then((res) => {
let promises = [];
res.forEach((datas) => { //this loop is for each and every response
datas.forEach((postData) => { //this loop is for
promises.push(api.create(postData));
});
});
Promise.all(promises).then((finalRes) => {
console.log("finalRes", finalRes);
});
});
const getNetwork = async () => {
const status = await Network.getStatus();
setStatus(status.connected);
console.log(networkStatus);
if (status.connected)
return true;
else
return false;
}
How can I get back the value from getNetwork Function?
Pretty much the same way you would handle any other promised based functions
const res = await getNetwork();
or
getNetwork().then((res) => {
// get response, handle the rest
})
Async operations returns Promise objects. If you return a Promise from a function like in your example and pass it to a variable directly its type will be Promise. As a result you will be able to call then() and catch() methods on it.
const res = getNetwork();
res.then(
(responseData) = doSomething();
)
But if you store the return value with await prefix, then it will return the resolved data
const res = await getNetwork();
console.log(res); // res will be equal responseData above
But you must be careful about errors, if it throws an error your code will crash. I personally prefer to encapsulate this process in a try-catch block and if I catch an error ı return a null value. Example code below
async getResponse(): Promise<object> { // object can be changed with your response type
try {
const res = await getNetwork();
return res;
} catch (e) {
return null;
}
}
I store some data in IndexedDB and i use npm package localforage for it.
const retrieveData = async () => {
const keys = await localforage.keys()
const data = await keys.map(async (key) => {
const item = await localforage.getItem(key)
return [item.username, item.compamy, item.email, item.lastUpdate]
})
return data
}
Whenever I execute this function, I get a resolved Promise object, which values I cannot extract
async componentDidMount() {
let asyncData = retrieveData()
console.log(asyncData) // Promise object
asyncData = retrieveData().then(values => values)
console.log(asyncData) // Promise object anyways
}
How exactly should I get data from this Promise object?
const retrieveData = async () => {
const keys = await localforage.keys()
// The return value of "keys.map" is an array of promises since
// async automatically returns a Promise behind the scenes.
// Await works on a single promise, not an array of promises,
// so "data" will not contain the actual data.
const data = await keys.map(async (key) => {
const item = await localforage.getItem(key)
return [item.username, item.compamy, item.email, item.lastUpdate]
})
return data
}
Do:
const retrieveData = async () => {
const keys = await localforage.keys()
const data = await Promise.all(keys.map(async (key) => {
const item = await localforage.getItem(key)
return [item.username, item.compamy, item.email, item.lastUpdate]
}));
return data
}
Or use Bluebird's map which works out of the box in this scenario:
// The "then" function does not do anything. It returns values,
// but only does so to the next "then" function. There are no
// further then-functions so the return value is unused.
// "values" is merely a local variable so you won't be able to
// access it anywhere outside the fat arrow function.
// You could move the console log into "then".
asyncData = retrieveData().then(values => values)
// asyncdata is still the unresolved promise object, the "then"
// function has not been run yet (then on the line above will be run when
// all of the awaits in retrieveData have been successfully resolved.
console.log(asyncData)
Do:
async componentDidMount() {
const data = await retrieveData();
console.log(data);
}
Or:
componentDidMount() {
retrieveData().then(values => {
console.log(values);
});
}
you can use for of loop here, and it will be simpler, we dont use await keyword directly in a map iterator, instead we can use promise.all as mentioned in above answer.
const retrieveData = async () => {
const keys = await localforage.keys();
let data;
for (let key of keys) {
const item = await localforage.getItem(key);
data.push([item.username, item.compamy, item.email, item.lastUpdate]);
}
return data;
}
Try using the "await" reserved keyword before your retrieveData() method at componentDidMount(), since it's a promise, an async event, you have to wait until it finishes all of it's inner executions to return some data and go on.
Just like you did at retrieveData() declaration use await before the promise. in detail what you need:
async componentDidMount() {
let asyncData = await retrieveData()
....
}