Passing axios.get URLs into axios.all based on options - javascript

I am building a vue app that will search YouTube channels based on which options are selected.
When the option is TRUE, I push that string into an array which holds the URLs of the axios.get() requests.
I am looping through that array and running axios.get() and returning the value. I am getting a response under Promise{} with and object inside [[PromiseValue]].
At the end I am combining the responses into a vue data element(catAndDogResults) but I am getting an undefined in the end.
Is there a better way to do what I am trying to do?
data () {
return {
catVideos: true,
dogVideos: true,
catResults: [],
dogResults: [],
catAndDogResults: []
}
},
methods:
search: function() {
var that = this
var cats = 'https://www.googleapis.com/youtube/v3/search?part=snippet&q=cats'
var dogs = 'https://www.googleapis.com/youtube/v3/search?part=snippet&q=dogs'
var urls = []
if (this.catVideos == true) {
urls.push(cats)
}
if (this.dogVideos == true) {
urls.push(dogs)
}
function getVideos() {
for (var i = 0; i < urls.length; i++) {
console.log(axios.get(urls[i])) // returns under [[PromiseValue]]:object
return axios.get(urls[i])
}
}
axios.all([
getVideos()
])
.then(axios.spread(function (catRes, dogRes) {
that.catResults = catRes.data.items
that.dogResults = dogRes.data.items
that.catAndDogResults = that.catResults.concat(that.dogResults)
}))
}
EDITS
Fixed spelling mistakes

Try changing your getVideos() method to return the array after the for loop:
function getVideos() {
var requests = [];
for (var i = 0; i < urls.length; i++) {
requests.push(axios.get(urls[i]));
}
return requests;
}
And then, call it like that:
axios.all(getVideos())
.then(function (catRes, dogRes) { ... })

The answer provided by #tiagodws should fix the issue. I just wanted to rewrite the getVideos function, you could write it using ES6 syntax like the following:
var getVideos = () => urls.map(url => axios.get(url))

Related

jest: expect.arrayContaining with order

How to match called with an array with order?
For example, I have tried, but failed as it ignore order:
expect(fn()).toBeCalledWith(expect.arrayContaining([1,2,3]))
My goal is success when calling with [1,2,3] [1,2,3,4] [1,10,2,7,8,9,3] but not [3,2,1]
The above code give me pass when called with [3,2,1], how can I achieve this goal?
This end up can be done with expect.extend, which I can create my custom matcher.
https://jestjs.io/docs/expect#expectextendmatchers
expect.extend({
arrayContainingWithOrder(received, sample) {
let index = 0;
for (let i = 0; i < received.length; i++) {
if (received[i] === sample[index]) {
index++;
}
}
const pass = index === sample.length;
if (pass) {
return {
message: () =>
`expected ${received} not to be contain ${sample} with order`,
pass: true,
};
} else {
return {
message: () =>
`expected ${received} to be contain ${sample} with order`,
pass: false,
};
}
},
});
You can convert your expected array and called argument to strings like 1,2,3. And then you can use expect.stringContaining instead of expect.arrayContaining. For example
const mockedFunction = jest.fn()
expect(mockedFunction).toHaveBeenCalled() //make sure your mocked function get called
const calledArray = mockedFunction.mock.calls[0][0] //get first argument which is your array
const expectedArray = [1,2,3]
const calledArrayString = calledArray.filter(value => expectedArray.includes(value)).join(",") //only keep contained values
const expectedArrayString = expectedArray.join(",")
expect(calledArrayString).toEqual(expect.stringContaining(expectedArrayString))

Pushing data in array inside for loop

Thanks in advance, i am trying to add request response in promises array inside for loop, currently CURRENCIES array have two values and forloop working good iterating for two times, but inside push i am unable to get value of CURRENCIES array at current index.
let promises = [];
for (var i = 0; i < CURRENCIES.length; i++) {
const api = 'https://api.etherscan.io/api';
promises.push(
axios.get(api, {
params: {
module: 'account', action: 'txlist',
address: '0x.........',
sort: 'asc', apikey: 'R6...........'
}
}).then((response) => {
console.log(CURRENCIES[i])
var data = {};
data[CURRENCIES[i]] = response.data.result
transactionsHistory.push(data);
})
)
}
In console.log for CURRENCIES[i] i always have undefined
The value of i is changing by the time the Promise resolves and you try to use it in the then function. You can be more explicit with it like this:
let promises = [];
for (var i = 0; i < CURRENCIES.length; i++) {
const api = "https://api.etherscan.io/api";
// setting a variable within the loop will make it valid within the scope.
const currency = CURRENICES[i];
promises.push(
axios
.get(api, {
params: {
module: "account",
action: "txlist",
address: "0x.........",
sort: "asc",
apikey: "R6..........."
}
})
.then(response => {
console.log(currency);
var data = {};
data[currency] = response.data.result;
transactionsHistory.push(data);
})
);
}
The problem was that you were using var which makes it a kind of global variable at the top level scope. Changing to let would make its scope more localized and should solve the problem too, but I like using a different variable within the loop to be more clear what is being used.

Can I use a for loop to create an array of promises?

In my Vue application, I'm using a for loop to iterate over an array of strings. Axios sends each element in the array to the API and receives the result of the call, which I append to an array. Unfortunately, the responses don't always arrive in the right order. Is there a way that I can track individual Axios requests so I can unscramble the responses later?
Edit: As per goto1’s comment below I have started using Promise.all() but I am receiving an error. The code has been changed to reflect this. Is the usage of the for loop creating this error?
Here's the code I have:
var app = new Vue({
el: "#app",
data() {
return {
a: "",
b: "Waiting...",
word: "",
array: [],
promises: [],
abc: []
};
},
methods: {
axi: function (p) {
//create array of promises
for (var x = 0; x < this.word.length; x++) {
this.promises.push(axios.get("https://googledictionaryapi.eu-gb.mybluemix.net/?define=" + this.word[x]));
}
//promise.all
Promise.all(this.promises)
},
//returns api call result for each item
multi: function () {
//this.a could be a string like “the quick brown fox”
var word = this.a;
//this.word turns into an array: ["the","quick","brown","fox"]
this.word = word.match(/\b(\w+)\b/g);
//each string is sent to the API
this.axi().then((response) => {
var r = response;
//I am hoping to get something like [“the”, "quick", "brown", "fox"]
console.log(r);
})
//ignore rest of code from here
.catch(function (error) {
console.log("Error! Could not reach the API. " + error);
});
},
//handles other stuff
getA: function () {
if (this.a === "") {
this.b = "Waiting...";
}
this.b = "Retrieving...";
//for another thing
this.array = [];
this.abc = [];
this.word = [];
//function call
this.multi();
}
}
});

React Axios API call with array loop giving wrong order?

I was learning react and doing some axios api call with an array. I did a code on gathering data through coinmarketcap api to learn.
So, my intention was to get the prices from the api with a hardcoded array of cryptocurrency ids and push them into an array of prices. But I ran into a problem with the prices array, as the prices were all jumbled up. I was supposed to get an array in this order
[bitcoinprice, ethereumprice, stellarprice, rippleprice]
but when I ran it in the browser, the prices came randomly and not in this order, sometimes I got my order, sometimes it didn't. I used a button which onClick called the getPrice method. Does anyone know what went wrong with my code? Thanks!
constructor(){
super();
this.state = {
cryptos:["bitcoin","ethereum","stellar","ripple"],
prices:[]
};
this.getPrice = this.getPrice.bind(this);
}
getPrice(){
const cryptos = this.state.cryptos;
console.log(cryptos);
for (var i = 0; i < cryptos.length; i++){
const cryptoUrl = 'https://api.coinmarketcap.com/v1/ticker/' + cryptos[i];
axios.get(cryptoUrl)
.then((response) => {
const data = response.data[0];
console.log(data.price_usd);
this.state.prices.push(data.price_usd);
console.log(this.state.prices);
})
.catch((error) => {
console.log(error);
});
}
}
If you want to receive the data in the order of the asynchronous calls you make, you can use Promise.all, that waits until all the promises of an array get executed and are resolved, returning the values in the order they were executed.
const cryptos = ['bitcoin', 'ethereum', 'stellar', 'ripple'];
const arr = [];
for (var i = 0; i < cryptos.length; i++){
const cryptoUrl = 'https://api.coinmarketcap.com/v1/ticker/' + cryptos[i];
arr.push(axios.get(cryptoUrl));
}
Promise.all(arr).then((response) =>
response.map(res => console.log(res.data[0].name, res.data[0].price_usd))
).catch((err) => console.log(err));
You could use a closure in the for loop to capture the value of i and use it as the index once the data is returned rather than using push:
getPrice(){
const cryptos = this.state.cryptos;
console.log(cryptos);
for (var i = 0; i < cryptos.length; i++) {
const cryptoUrl = 'https://api.coinmarketcap.com/v1/ticker/' + cryptos[i];
(function (x) {
axios.get(cryptoUrl)
.then((response) => {
const data = response.data[0];
console.log(data.price_usd);
var newPrices = this.state.prices;
newPrices[x] = data.price_usd;
this.setState({prices: newPrices});
console.log(this.state.prices);
})
.catch((error) => {
console.log(error);
});
})(i);
}
}

How to get many JSONs by Javascript or JQuery asynchronously

I'd like to get many JSONs by Javascript or JQuery,
and pull of elements named j_name, j_value, and j_sts into sarr[i], rarr[i], and parr[i], respectively.
var sarr = new Object;
var rarr = new Object;
var parr = new Object;
//num_json is the number of JSONs
//jsonurl is a url of the JSON
for(var i = 1; i < num_json; i++){
$.ajaxSetup({async: false});
$.getJSON(jsonurl[i], function(data) {
sarr[i] = data.j_name;
rarr[i] = data.j_value;
parr[i] = data.j_sts;
});
$.ajaxSetup({async: true});
}
Without
$.ajaxSetup({async: false});
and
$.ajaxSetup({async: true});
, an asynchronous processing causes that sarr[i], rarr[i], and parr[i] are filled by current loaded JSON elements.
I have to stop asynchronous loading. However, this process is very slow to display the page.
I'd like to do like following to separate the object "data" storing a JSON, but it's clearly invalid.
var sarr = new Object;
var rarr = new Object;
var parr = new Object;
var data = new Object;
for(var i = 1; i < num_json; i++){
$.getJSON(jsonurl[i], function(data[i]) {
sarr[i] = data[i].j_name;
rarr[i] = data[i].j_value;
parr[i] = data[i].j_sts;
});
}
How can I load many JSONs asynchronously and store each specific elements?
Here is a way to do it
const urls = [];
const responses = urls.map( url => fetch(url).then(res => res.json()) );
Promise.all(responses)
.then( dataArr => {
return dataArr.reduce((res, data) => {
res.sarr[i] = data[i].j_name;
res.rarr[i] = data[i].j_value;
res.parr[i] = data[i].j_sts;
return res;
}, { sarr: [], rarr: [], parr: [] });
} )
.then(result => {
const { sarr, rarr, parr } = result;
});
This solutions uses the fetch API and Promises.
Basically what it does is : given an array of urls, make a request for each urls and retrieve the result as json, store the result in an array of Promises.
Given this array of promises, once all are settled, retrieve the result if and only if all requests were successful. If they are successful then store each values in the correct array and return it.

Categories