http request await sync in repetitive function - javascript

I have a problem with a repetitive function with an HTTP request.
When I call the getDocument() function, a result appears at the end of the console but unfortunately not in the frontend. I get a new json array by the call where also the render() function should be applied.
I render the ouput after each run of the render() function in the HTML view.
It seems to me that the HTTP.request is too slow and can't keep up with the repeating function. I have read up a bit on async await but am still having problems using it correctly.
var jsonArray = [
{
etype: 'normal',
content: 'renderContent'
},
{
etype: 'extern',
id: 'MyIdToDocument',
subcontent: [
{
etype: 'normal',
content: 'renderContent'
}
]
},
{
etype: 'normal',
content: 'renderContent'
}
]
jsonArray.forEach(object => {
this.render(object)
});
function render(object) {
if(object.etype ==='normal') {
object.subcontent.forEach( subcontentItem => {
output = this.render(subcontentItem)
})
}
if(object.etype ==='extern') {
output = this.getDocument(object.id)
}
return output;
}
function getDocument(id) {
this.httpClient.post('myAPiUrl/', id).subscribe(response => {
response.forEach(object => {
this.render(object)
});
})
}

Related

Pinia|Vue3 I can't access the property of the object that returned from the Pinia action

first of all I am using the Mockjs to simulate the backend data:
{
url: "/mockApi/system",
method: "get",
timeout: 500,
statusCode: 200,
response: { //
status: 200,
message: 'ok',
data: {
'onlineStatus|3': [{
'statusId': '#integer(1,3)',
'onlineStatusText': '#ctitle(3)',
'onlineStatusIcon': Random.image('20*20'),
'createTime': '#datetime'
}],
'websiteInfo': [{
'id|+1': 1,
}]
}
}
}
the data structure would be: https://imgur.com/a/7FqvVTK
and I retrieve this mock data in Pinia store:
import axios from "axios"
import { defineStore } from "pinia"
export const useSystem = defineStore('System', {
state: () => {
return {
systemConfig: {
onlineStatus: [],
},
}
},
actions: {
getSystemConfig() {
const axiosInstance = axios.interceptors.request.use(function (config) {
// Do something before request is sent
config.baseURL = '/mockApi'
return config
}, function (error) {
// Do something with request error
return Promise.reject(error);
})
axios.get('/system/').then(res => {
this.systemConfig.onlineStatus = res.data.data.onlineStatus
})
// console.log(res.data.data.onlineStatus)
axios.interceptors.request.eject(axiosInstance)
}
}
})
I use this store in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]
},
},
components: {UserOnlineStatus }
}
template in Profile.vue I import the child component userOnlineStatus.vue
<UserOnlineStatus :userCurrentOnlineStatus="userData.onlineStatus">
{{ showUserOnlineStatusText }}
</UserOnlineStatus>
here is what I have got https://imgur.com/fq33uL8
but I only want to get the onlineStatusText property of the returned object, so I change the computed code in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]['onlineStatusText']//👀I chage it here!
},
},
components: {UserOnlineStatus }
}
but I will get the error in the console and it doesn't work:
https://imgur.com/Gb68Slk
what should I do if I just want to display the specific propery of the retrived data?
I am out of my wits...
I have tried move the store function to the child components, but get the same result.
and I google this issue for two days, nothing found.
Maybe it's because of I was trying to read the value that the Profile.vue hasn't retrieved yet?
in this case, how could I make sure that I have got all the value ready before the page rendered in vue3? Or can I watch this specific property changed, then go on rendering the page?
every UX that has data is coming from remote source (async data) should has spinner or skeleton.
you can use the optional chaining for safe access (if no time to await):
return this.getUserOnlineStatus?.[this.getUserOnlineStatusIndex - 1]?.['onlineStatusText']

Follow on actions after asynch function completion

I'm going to do my best to explain this one since this is a reduced version of the real code and some parts might seem redundant or excessive (and they might be). The model can be found at https://jsfiddle.net/ux5t7ykm/1/. I am using a function to build an array of values that are passed to another function to do a REST call for the data that corresponds to the values. The array lists the name of the list to get the data from, the field name, the filter field name, the filter value, and the target array to pass the autocomplete values back to. This part is working fine and is essentially what Step1() does. setAutocomplete() then uses getData() to make the multiple REST calls. I'm using sprLib for that part so it's already a promise (this is getListItems() normally). getData() is where the magic is happening. It recursively cycles through the list and performs getListItems() until all the data is retrieved. I have this set as an async function with await. This also seems to be running fine.
The problem I have is that once this whole sequence is complete, I then have another function that I need to run. It manipulates the data from the previous function. I've tried to put step1() in a promise, make it async, etc. Nothing seems to work. CheatStep() seems to be where a response or some other method signaling to Step1() should go to then allow the next function to run. I've been banging my head against this all week and I feel like my understanding of promises and asnyc is regressing.
var dict = [{
'id': '1',
'title': 'test1'
}, {
'id': '2',
'title': 'test2'
}, {
'id': '3',
'title': 'test3'
}, {
'id': '4',
'title': 'test4'
}, ]
step1()
nextStep()
function nextStep() {
console.log('I run after Step1')
}
function cheatStep() {
console.log('I cheated')
}
function step1() {
setAutoComplete(4);
}
function setAutoComplete(listIndex) {
getData(listIndex,
function(result) {
for (var i = 0; i < listIndex; i++) {
var autoDict = result[i];
console.log(autoDict)
}
cheatStep()
},
function(e) {
console.error(e);
alert('An error occurred wile setting autocomplete data for item');
})
}
async function getData(listIndex, success, error, curIndex, result) {
var curIndex = curIndex || 0;
var result = result || {};
await getListItems(curIndex,
function(listItems) {
result[curIndex] = listItems;
curIndex++;
if (listIndex > curIndex) {
getData(listIndex, success, error, curIndex, result);
console.log('1');
} else {
console.log('2');
success(result);
}
},
error)
}
function getListItems(curIndex, success, error) {
new Promise(
function(resolve, reject) {
var x = dict[curIndex];
resolve(x);
})
.then(function(x) {
success(x);
});
}
I have adopted your code to a more async/await view coz you still use heavily callback approach:
var dict = [{
'id': '1',
'title': 'test1'
}, {
'id': '2',
'title': 'test2'
}, {
'id': '3',
'title': 'test3'
}, {
'id': '4',
'title': 'test4'
}, ]
function nextStep(result) {
console.log('I run after Step1',result)
}
function cheatStep() {
console.log('I cheated')
}
step1().then((result)=>{nextStep(result)})
async function step1() {
return await setAutoComplete(4);
}
async function setAutoComplete(listIndex) {
const result = await getData(listIndex);
for (var i = 0; i < listIndex; i++) {
var autoDict = result[i];
console.log(autoDict)
}
cheatStep()
return result
}
async function getData(listIndex, curIndex=0, result={}) {
const listItems = await getListItems(curIndex)
result[curIndex] = listItems;
curIndex++;
if (listIndex > curIndex) {
return await getData(listIndex, curIndex, result);
} else {
console.log('2');
return (result);
}
}
function getListItems(curIndex) {
return new Promise(
(resolve, reject) =>{
resolve(dict[curIndex]);
})
}
I also kept recursion here, but personally I would prefer solution with a loop instead.
Also a minor advise. Try to avoid write declaration after usage:
nextStep()
function nextStep() {
console.log('I run after Step1')
}
JS hoisting does that a valid syntax but it is really hard to read=)

Proxying an object in Javascript: The results from the proxied function are not being applied

What I am trying to do is intercept a function call, and prepend a header. The header is getting applied, the function itself is getting called, but the results don't have actually body.
code:
'use strict'
const load = require('require.all')
module.exports = ({xr}) => {
const cmds = load({
dir: __dirname,
not: /index\.js$/i
})(xr);
const result = Object.entries(cmds).map(([k,v]) => {
return {
[k] : new Proxy(v, {
get: function (target, prop) {
return (...args) => {
return {
type: 'cmd',
...(target[prop])(args)
}
}
},
})
}
}).reduce((a,b) => Object.assign({},a,b))
return result
}
The original function will return something like:
{
blocks:[...]
}
and I need to prepend this header.
expected output:
{
type: "cmd",
blocks: [...]
}
I would give you what I am getting as JSON except, it chokes on JSON.stringify because of this proxy. However, in my debugger (and my code agrees because it blows up) the only thing there is type: "cmd"
Am I doing this right?

Vue data not updated in created()

I'm trying to transform an API response into a more useful form for building two tables. When I add debugging outputs inside my function in created(), I can see the desired output, but examining the data after the fact, it seems to have not changed. I suspect that something weird is happening with this, but I can't seem to sort it out.
Here's what I have so far:
export default {
name: 'component',
data: function() {
return {
tableOne: [],
}
},
computed: {
...mapState([
'modal'
])
},
created() {
api.get_appointments()
.then(appointments => {
for (var i = 0; i < appointments.length; i++) {
this.tableOne.push(
{
tech: appointments[i].tech_name,
date: appointments[i].scheduled_date
}
)
}
});
},
};
api.get_appointments() includes the following:
get_appointments() {
return axios({
method: "get",
url: '/appointments'
})
.then(res => (res.data.data))
.catch(error => {return error});
};
Since it's doing a request hit..the second console prints before the request is resolved. Try using async-await
async created() {
await api.get_appointments()
.then(appointments => {
for (var i = 0; i < appointments.length; i++) {
this.tableOne.push(
{
tech: appointments[i].tech_name,
date: appointments[i].scheduled_date
}
)
}
// console.log(this.tableOne);
});
// console.log(this.tableOne);
},

My function is not loading in the right way

When I load my page my datacollection is null or undefined, but I want my data from the api to be filled in as soon as the site loads.
<script>
import LineChart from './LineChart.js'
import axios from 'axios'
export default {
name: 'Graph',
components: {
LineChart
},
data () {
return {
ratingOne: null,
ratingTwo: null,
ratingThree: null,
datacollection: null
}
},
created: function () {
console.log(this.datacollection)
this.fillData()
},
mounted () {
},
methods: {
getZeroes () {
axios.get('MYAPI').then(response => {
this.ratingOne = response.data.Rates.filter(rates =>
rates.rate === 0).length
return this.ratingOne
})
},
fillData () {
this.getOnes()
console.log(this.getZeroes())
this.getOnes()
this.getTwos()
this.datacollection = {
labels: ['DÃ¥rlig oplevelse', 'Okay oplevelse', 'Rigtig god
oplevelse'],
datasets: [
{
backgroundColor: ['#FF0000', '#D3D3D3', '#00CC00'],
data: [this.ratingOne, this.ratingTwo, this.ratingThree]
}
]
}
}
}
}
</script>
When i use a click function it works and it loads my graph with the data I want, but it does not work when my page I loaded. Can anyone tell me why?
When i console log "this.getZeroes())" it just tells me "undefined"
and when i click my button with update
<button #click="fillData()">Randomize</button>
it works
getZeroes, getOnes, getTwos are asynchronous functions. After getZeroes function and such are run, the data is not ready. That's why dataCollection doesn't have the right data.
You need to you promise to wait for the response finish, and update the data to the chart.
getZeroes will return a promise.
getZeroes () {
return axios.get('MYAPI').then(response => {
this.ratingOne = response.data.Rates.filter(rates => rates.rate === 0).length
return this.ratingOne
})
},
in the fillData. We must wait them to finish by using then
fillData () {
this.getOnes()
.then(() => this.getZeroes())
.then(() => this.getTwos())
.then(() => {
this.datacollection = {
labels: ['DÃ¥rlig oplevelse', 'Okay oplevelse', 'Rigtig god oplevelse'],
datasets: [
{
backgroundColor: ['#FF0000', '#D3D3D3', '#00CC00'],
data: [this.ratingOne, this.ratingTwo, this.ratingThree]
}
]
}
})
}
Or we can even run the gets parallel and get data at once by using Promise.all
Promise.all([this.getOnes(), this.getTwos(), this,getThrees()])
.then(() => { /* the rest here */ })

Categories

Resources