Hanging in a vue component with async / await call - javascript

I make my first steps with the vue framework and I can't figure out to solve the following problem.
dateClick(arg)
{
this.cal.date = arg.date;
this.cal.title = arg.resource.title;
this.cal.resource = arg.resource.id;
// const slots = (async() => {
// return await dataService.getSlots(arg);
// })().catch(console.error);
// console.log(slots);
(async() => {
const slots = await dataService.getSlots(arg);
console.log(slots);
})().catch(console.error);
}
The console.log in the async function works correct and returns the slots. Finally I would need to also set a data attribute in the current Vue Component like this.cal.slots = slots. But that doesn't work, always undefined. I also tried the commented code above to return the await - this would result in "Promise {pending}". Can't figure it out how to solve this?

Try like this:
async dateClick(arg) {
...
this.cal.slots = await dataService.getSlots(arg);
...

Related

Declarations in script setup return undefined variables after async call with top level await

My Nuxt 3 / Vue 3 component with script setup looks like:
<script setup>
const content = await useFetch('/api/items/published').then((res) => res.data)
const items = await content.data.items
const issues = await content.data.issues
</script>
<template>
<Gallery :items="items" :layout="layout" #sponsorModalOpen="sponsorModalOpen()" />
</template>
The variables items and issues are returned as Undefined presumably because they are not awaiting the result of the first async call with useFetch before being set. This seems unintuitive as I would have expected the await on both the useFetch operation and the variable declarations to block until the pre-requisite task has finished.
If I re-write the code to pass content directly to the Gallery component as below, it works:
<Gallery :items="content?.items" :layout="layout" #sponsorModalOpen="sponsorModalOpen()" />
though notable this only works with the optional chaining as it is presumably rendering initially with the prop undefined, then updating once the async fetch operation completes.
Am I using await wrong here? How can I ensure that the variables aren't set until the async operation completes so that I can manipulate them (specifically to re-order the results)?
The only solution I have found to this so far is to do the sorting directly in the then() callback on the initial fetch as below:
const content = await useFetch('/api/items/published').then((res) => {
const sortedItems = res.data.value.items.sort((a, b) => {
return b.issueNumber - a.issueNumber
});
console.log(res.data.value.issues)
const sortedIssues = res.data.value.issues.sort((a, b) => {
const dateA = new Date(a.publishDate)
const dateB = new Date(b.publishDate)
console.log(dateA, dateB)
return dateB - dateA
});
return {
sortedItems: sortedItems,
sortedIssues: sortedIssues
}
})
It may be that using Suspense would also be a solution, but I have not yet looked into this.

Jest, mocking return from await, I see results in console, but expect still fails

I have a pretty simple example of a mock working, updating the value correctly, but my test still fails.
fetchStuff.js:
const fetchStuff = async (entity, service) => {
entity = await service.get();
console.log('entity is an expected result!', entity); // this consoles out the expected value correctly.
}
fetchStuff.spec.js:
it('should...', () => {
const expected = 'this will show up in console';
const service.get = jest.fn().mockImplementation(() => expected);
let entity;
fetchStuff(entity, service);
expect(entity).toEqual(expected) // entity = 'undefined'
});
Every time, I see that console.log in the function is printing out the expected result, but some reason the expect(entity) is always undefined.
I've tried stuff like with flush-promise, I've tried removing the await. I've even seen the done(), technique, where we pass done in to it, ie it('should...', done => {...})
Not sure why I cannot get the correct expected, even tho the console is showing the expected result.
PS. I understand this is not respecting functional paradigm or pure functions. Please ignore that.
You are using an asynchronous function, but testing right away, before the promise is resolved. You need an async/await in your test, if you are using asynchronous calls, like your await in the main function.
it('should...', async () => {
const expected = 'this will show up in console';
const service.get = jest.fn().mockImplementation(() => expected);
let entity;
await fetchStuff(entity, service);
expect(entity).toEqual(expected) // entity = 'undefined'
});
However, as per the comment on your question, the code may not be returning what you expect, so this change for async/await will not solve the problem on its own.
const fetchStuff = async (entity, service) => {
entity = await service.get();
console.log('entity is an expected result!', entity);
return entity;
}
it('should...', async () => {
const expected = 'this will show up in console';
const FakeService = jest.fn().mockResolvedValue(expected),
const entity = await FakeService();
expect(entity).toBe(expected) ;
});

Axios get function undefined when accessing outside, cannot use async and await

The following code gives me undefined. I tried fixing it with various async/await but it came up as an error. Anyone know how to fix? EDIT: To be more specific, I need the value of "output" stored. I am using console.log just to guarantee it is getting the information I need it to have.
const URL = 'https://api.1inch.exchange/v3.0/1/quote?fromTokenAddress=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&toTokenAddress=0x6b175474e89094c44da98b954eedeac495271d0f&amount=1&protocols=';
function getAmount(){
axios.get(URL).then((response) => {
//console.log(response.data)
const output = response.data.toTokenAmount
//console.log(output)
return output
})
//console.log(output)
}
console.log(getAmount());
You must return promise inside getAmountfunction. Also you need to wait on that promise when calling getAmount function.
This should work:
const URL = 'https://api.1inch.exchange/v3.0/1/quote?fromTokenAddress=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&toTokenAddress=0x6b175474e89094c44da98b954eedeac495271d0f&amount=1&protocols=';
function getAmount(){
return axios.get(URL).then((response) => {
const output = response.data.toTokenAmount
return output; })
}
getAmount().then(toTokenAmount => console.log(toTokenAmount));

Nodejs - array not returning anything

I'm trying to save the answer to my request with axios in an array, but I'm not getting it, is something wrong?
colorList = axios.get(colors);
let codes = [];
for (let i = 0; i < colorList.data.data.colors.length; i++) {
codes[i].push(colorList.data.data.colors[i]);
}
console.log(codes);
The call is asynchronous meaning that you have to wait for your request to complete (or more accurately your promise from axios.get() to resolve) and do something with the result. Your code right now runs synchronously.
colorList = axios.get(colors).then(result =>{
console.log(result)
});
EDIT: Or as a comment above noted, use an async/await setup. Keep in mind that you can't use await in top level code, it can only be used inside an async function
(async () => {
try {
const colorCodes = await axios.get(colors);
} catch (e) {
// handle error
}
})()
If colorList.data.data.colors has values then try
codes.push(colorList.data.data.colors[i]);
As most comments pointed out, the mistake in your code is that you don't await for the async call to be completed.The way that js works when you use async code is that it jumps to the next line immediatelly while the async process continues . That's the point of Async code: You don't want your program to freeze in case of async call delays. I made a similar code example using numbersapi.
var axios = require('axios');
const axiosCallToArray = async () => {
const result = await axios.get('http://numbersapi.com/42');
const wordsArray = result.data.split(' '); // here i create an array breaking the sentence (returned as result from axios) into words.Every word is an element of the new array
console.log(wordsArray);
};
axiosCallToArray(); //now console.log works fine. This is valid also for your code

Recursion with an API, using Vanilla JS

I'm playing with the Rick and Morty API and I want to get all of the universe's characters
into an array so I don't have to make more API calls to work the rest of my code.
The endpoint https://rickandmortyapi.com/api/character/ returns the results in pages, so
I have to use recursion to get all the data in one API call.
I can get it to spit out results into HTML but I can't seem to get a complete array of JSON objects.
I'm using some ideas from
Axios recursion for paginating an api with a cursor
I translated the concept for my problem, and I have it posted on my Codepen
This is the code:
async function populatePeople(info, universePeople){ // Retrieve the data from the API
let allPeople = []
let check = ''
try {
return await axios.get(info)
.then((res)=>{
// here the current page results is in res.data.results
for (let i=0; i < res.data.results.length; i++){
item.textContent = JSON.stringify(res.data.results[i])
allPeople.push(res.data.results[i])
}
if (res.data.info.next){
check = res.data.info.next
return allPeople.push(populatePeople(res.data.info.next, allPeople))
}
})
} catch (error) {
console.log(`Error: ${error}`)
} finally {
return allPeople
}
}
populatePeople(allCharacters)
.then(data => console.log(`Final data length: ${data.length}`))
Some sharp eyes and brains would be helpful.
It's probably something really simple and I'm just missing it.
The following line has problems:
return allPeople.push(populatePeople(res.data.info.next, allPeople))
Here you push a promise object into allPeople, and as .push() returns a number, you are returning a number, not allPeople.
Using a for loop to push individual items from one array to another is really a verbose way of copying an array. The loop is only needed for the HTML part.
Also, you are mixing .then() with await, which is making things complex. Just use await only. When using await, there is no need for recursion any more. Just replace the if with a loop:
while (info) {
....
info = res.data.info.next;
}
You never assign anything to universePeople. You can drop this parameter.
Instead of the plain for loop, you can use the for...of syntax.
As from res you only use the data property, use a variable for that property only.
So taking all that together, you get this:
async function populatePeople(info) {
let allPeople = [];
try {
while (info) {
let {data} = await axios.get(info);
for (let content of data.results) {
const item = document.createElement('li');
item.textContent = JSON.stringify(content);
denizens.append(item);
}
allPeople.push(...data.results);
info = data.info.next;
}
} catch (error) {
console.log(`Error: ${error}`)
} finally {
section.append(denizens);
return allPeople;
}
}
Here is working example for recursive function
async function getAllCharectersRecursively(URL,results){
try{
const {data} = await axios.get(URL);
// concat current page results
results =results.concat(data.results)
if(data.info.next){
// if there is next page call recursively
return await getAllCharectersRecursively(data.info.next,results)
}
else{
// at last page there is no next page so return collected results
return results
}
}
catch(e){
console.log(e)
}
}
async function main(){
let results = await getAllCharectersRecursively("https://rickandmortyapi.com/api/character/",[])
console.log(results.length)
}
main()
I hesitate to offer another answer because Trincot's analysis and answer is spot-on.
But I think a recursive answer here can be quite elegant. And as the question was tagged with "recursion", it seems worth presenting.
const populatePeople = async (url) => {
const {info: {next}, results} = await axios .get (url)
return [...results, ...(next ? await populatePeople (next) : [])]
}
populatePeople ('https://rickandmortyapi.com/api/character/')
// or wrap in an `async` main, or wait for global async...
.then (people => console .log (people .map (p => p .name)))
.catch (console .warn)
.as-console-wrapper {max-height: 100% !important; top: 0}
<script>/* dummy */ const axios = {get: (url) => fetch (url) .then (r => r .json ())} </script>
This is only concerned with fetching the data. Adding it to your DOM should be a separate step, and it shouldn't be difficult.
Update: Explanation
A comment indicated that this is hard to parse. There are two things that I imagine might be tricky here:
First is the object destructuring in {info: {next}, results} = <...>. This is just a nice way to avoid using intermediate variables to calculate the ones we actually want to use.
The second is the spread syntax in return [...results, ...<more>]. This is a simpler way to build an array than using .concat or .push. (There's a similar feature for objects.)
Here's another version doing the same thing, but with some intermediate variables and an array concatenation instead. It does the same thing:
const populatePeople = async (url) => {
const response = await axios .get (url)
const next = response .info && response .info .next
const results = response .results || []
const subsequents = next ? await populatePeople (next) : []
return results .concat (subsequents)
}
I prefer the original version. But perhaps you would find this one more clear.

Categories