Axios sequential execution using promise.all - javascript

I am dynamically executing API callouts using promise all , but I want to control the order of execution of the callouts.
So in the code below, I want callout2 to wait until callout1 is complete and 3 to wait for 2.
dataMap = [callout1, callout2, callout3]
const newMap = dataMap((x) =>{
req = axios.post(url, {
"query": x
})
return req;
});
Promise.all(promiseArray)
.then( (val) => {
console.log(val[0]);
console.log(val[1]);
console.log(val[2]);
})
.catch( (error) => console.log('error' + error))

Related

Wait for response from request before returning

I am trying to create a function with a GET request that returns a portion of the data from the GET request. However, it keeps returning before the data is retrieved, so I keep getting "undefined". How can I set this up so it actually waits for the data to be set before returning?
let getInfo = async () => {
const request = net.request({
url: URL
})
return new Promise((resolve, reject) => { // Promise being here DOES work
request.on('response', (response) => {
response.on('data', (chunk) => {
//return new Promise((resolve, reject) => { //Promise being here does NOT work
let body = JSON.parse(chunk)
let info = body.data
if (info){
resolve(info);
}
reject();
//})
});
});
request.write('')
request.end()
}).then(data => {
console.log("From then: "+data)
return data
})
}
getInfo().then(data => {
console.log("From outside: "+data)
})
Edit: This is the updated version that still does not work. I am trying to use the native electron method and I don't see why this doesn't work. The "From then:" part displays the info correctly. But when run "From outside:" it prints undefined. Does the issue have anything to do with the response.on being nested inside the request.on?
Solution: As #NidhinDavid showed in his answer, the issue was that the promise was inside the 'response' listener. Moving the 'GET' request from start to finish inside the Promise fixed it to giving the correct output. I have updated my code to reflect that for future individuals.
let getInfo = () => {
let info;
const request = net.request({
url: URL
})
return new Promise((resolve, reject) => {
request.on('response', (response) => {
response.on('data', (chunk) => {
request.write('')
request.end()
let body = JSON.parse(chunk)
info = body.data
if (info) {
resolve(info)
} else {
reject('Something went wrong');
}
});
});
})
}
getInfo()
.then(data => {
// this will be your info object
console.log(data)
})
.catch(err => {
// this will log 'Something went wrong' in case of any error
console.log(err)
})
You need to return inside your, on type event handler. Read more about asynchronous code and synchronous code here
I couldn't find the net module and the one which is included with Nodejs do not have request method. So to get the similar concept of event emiters and promise I am using http module and doing a http request to fetch json and parse it
'use strict'
var https = require('https');
const getInfo = async () => {
// create a new promise chain
// remember it is a chain, if one return is omitted
// then the chain is broken
return new Promise((resolve, reject) => {
var options = {
host: 'support.oneskyapp.com',
path: '/hc/en-us/article_attachments/202761727/example_2.json'
};
// start the request
https.request(options, function (response) {
var str = '';
// data arrives in chunks
// chunks needs to be stitched together before parsing
response.on('data', function (chunk) {
str += chunk;
});
// response body obtained
// resolve (aka return) the result
// or parse it, or do whatever you want with it
response.on('end', function () {
resolve(str)
});
// errors are another event
// listen for errors and reject when they are encountered
response.on('error', function (err) {
reject(err)
})
}).end()
})
}
//*********************************************
// using async await
//*********************************************
// if this is the entry point into app
// then top-level async approach required
(async ()=>{
try{
let data = await getInfo()
console.log("From ASYNC AWAIT ")
console.log(JSON.stringify(JSON.parse(data)))
}
catch (err) {
console.log("operation failed, error: ", err)
}
})();
//************************************************
// using promise chains
//************************************************
getInfo()
.then((data)=>{
console.log("FROM PROMISE CHAIN ")
console.log(JSON.stringify(JSON.parse(data)))
})
.catch((err)=>{
console.log("operation failed, error: ", err)
})
Tyr this, it might works for you,
let info;
const getInfo = async (_url)=>{
const response = await fetch(_url);
const data = await response.json();
info = data;
} ;
const url = "some url";
getInfo(url);
console.log(info);
Async function always returns a promise, so either consume that promise or internally await the data and assign it to some variable.
Check for the valid data required in info by logging it to the console.

How to fetch data on every element in an array using array.map method

I want to fetch data for every object in an array and return an array of new objects with the previous and newly fetched data.I got stucked on getting my result array as my function is returning an array of resolved undefined promises.
I am using a flight search api thats using the apca function for fetching
export const searchApcaLocation = async (dataArr,setDeals) => {
const promises = await dataArr.map(async item => {
apca.request(item.destination);
apca.onSuccess = (data) => {
return fetch('http://localhost:3050/googlePlaceSearch',{
method:"post",
headers:{'Content-Type':'application/json'},
body:JSON.stringify({
cityName:data.airports[0].city
})
})
.then(res => res.json())
.then(imagelinkData => {
const locationObject = {
data: item,
imagelink: imagelinkData.link
}
return locationObject
})
.catch(err => console.log('error on image search',err))
};
apca.onError = (data) => {
console.log('error',data)
};
})
const results = await Promise.all(promises)
return results
}
can someone guide me please on what am I doing wrong?
edit:
as I am trying to fix it realized the problem is I am not returning anything in my map function but if trying to return the apca.onSuccess I am getting an array of functions
just return is missing before fetch function. since you're not returning your promise result it's giving undefined.
export const searchApcaLocation = async (dataArr,setDeals) => {
const promises = await dataArr.map(async item => {
apca.request(item.destination);
apca.onSuccess = (data) => {
return fetch('http://localhost:3050/googlePlaceSearch',{
method:"post",
headers:{'Content-Type':'application/json'},
body:JSON.stringify({
cityName:data.airports[0].city
})
})
.then(res => res.json())
.then(imagelinkData => {
const locationObject = {
data: item,
imagelink: imagelinkData.link
}
return locationObject
})
.catch(err => console.log('error on image search',err))
};
apca.onError = (data) => {
console.log('error',data)
};
})
const results = await Promise.all(promises)
return results
}
The issue in your case might be, that you are using async/await and then blocks together.
Let me sum up what is happening :
1) you await dataArray.map
2) within the map callback, you use the onSuccess method of apca
3) within this method you are using then blocks which won't await until you got a response.
At this point where you return the locationObject, your function already reached the return statement and tries to return results.
But results are of course undefined because they never get resolved at all.
Also, keep in mind that your function returns another promise because you used async/await which you have to resolve where you imported it.
Cheers :)

Fetching multiple files using Promises and Fetch API javascript

I am updating my javascript skills with Promises, already have in place a library with XHR and callbacks to load and inject multiple files at once and only proceed if ALL of them succeeded.
I am trying to use Promise.all() and Fetch API to get a similar functionality but can't make it work: console.log('All the promises are resolved', values); always triggers no matter if some of the fetch promises failed.
I want to be able to execute the code below, and only proceed with nowInitialize function if all the files were able to be fetched, or throw error using catch() with the reason of the first file that failed
xRequire(['index.html', 'style.cds'])
.then(nowInitialize)
.catch(reason => 'One or more files failed to load' + reason)
style.cds will obviously fail
//TODO Handle file types appropriately
//TODO: Inject css, javascript files
function xRequire(files) {
let urls = [];
let promisesList = [];
let handleAllPromises;
//Populates urls with each file required
for(let i=0; i < files.length ; i++) {
urls.push(files[i]);
}
//Fetch each file in urls
urls.forEach( (url, i) => { // (1)
promisesList.push(
fetch(url)
.then(handleResponse)
.then(data => console.log(data))
.catch(error => console.log(error))
);
});
handleAllPromises = Promise.all(promisesList);
handleAllPromises.then(function(values) {
console.log('All the promises are resolved', values);
});
handleAllPromises.catch(function(reason) {
console.log('One of the promises failed with the following reason', reason);
});
}
function handleResponse(response) {
let contentType = response.headers.get('content-type');
console.log('Requested Info: ' + contentType);
if (contentType.includes('application/json')) {
return handleJSONResponse(response);
} else if (contentType.includes('text/html')) {
return handleTextResponse(response);
} else if (contentType.includes('text/css')) {
return handleTextResponse(response);
} else if (contentType.includes('application/javascript')) {
return handleTextResponse(response);
} else {
throw new Error(`Sorry, content-type ${contentType} not supported`);
}
}
function handleJSONResponse(response) {
return response.json()
.then(json => {
if (response.ok) {
return json;
} else {
return Promise.reject(Object.assign({}, json, {
status: response.status,
statusText: response.statusText
}));
}
});
}
function handleTextResponse(response) {
return response.text()
.then(text => {
if (response.ok) {
return text;
} else {
return Promise.reject({
status: response.status,
statusText: response.statusText,
err: text
});
}
});
}
Can you just rewrite it as async-await code? Here is a rough idea of the typical flow:
const [data1, data2, data3] = await Promise.all([
fetch(url1),
fetch(url2),
fetch(url3),
]);
In other words, Promise.all() returns the promise to all the data that is returned from your multiple fetch() functions.
Then, if you put this into a try-catch, you can handle the rejection as well:
try {
const [data1, data2, data3] = await Promise.all([
fetch(url1),
fetch(url2),
fetch(url3),
]);
// Now you can process the data:
[data1, data2, data3].map(handleResponse);
} catch (error) {
console.log('Error downloading one or more files:', error);
}
If you want to loop with async-await, you can do that:
const promises = [];
for (const url of [url1, url2, url3, url4]) {
promises.push(fetch(url));
}
const [data1, data2, data3, data4] = await Promise.all(promises);
There are two problems. First, you need to return the Promise.all call from xRequire in order to consume it in your xRequire(..).then:
return Promise.all(promisesList);
Also, when you use .catch, if a Promise is initially rejected, it will go into the catch block, do whatever code is there, and then the Promise chain will resolve (not reject) to whatever the catch block returns. If you want to percolate errors up the Promise chain, put your catch at the point in the chain at which you want to detect errors:
urls.forEach( (url, i) => { // (1)
promisesList.push(
fetch(url)
.then(handleResponse)
.then(data => console.log(data))
// no catch here
);
});
I would suggest putting your catch only in the caller of xRequire, that way it will see all errors. Your xRequire function can be reduced to:
xRequire(['index.html', 'style.cds'])
.then(nowInitialize)
.catch(reason => 'One or more files failed to load' + reason)
function xRequire(files) {
return Promise.all(
urls.map(handleResponse)
);
}
If you want the body of xRequire to be able to see errors, but you also want to percolate errors up the Promise chain, throw an error in a catch inside xRequire, so that the Promise it resolves to will reject, rather than resolve:
function xRequire(files) {
return Promise.all(
urls.map(handleResponse)
)
.catch((err) => {
console.log('There was an error: ' + err);
throw err;
})
}
I finally solved it in this way --with the only quirk i've found so far: files argument always needs to be an array, therefore always needs brackets when calling the function--
xRequire(['my-file'])
.then(handle success)
.catch(handle error);
async function xRequire(files) {
let promises = [];
let receivedData;
//Iterate over files array and push results of fetch to promises array
files.map(x => promises.push(fetch(x)));
//populate receivedData array from promises array
receivedData = await Promise.all(promises);
//iterate over receivedData to handle each response accordingly
return receivedData.map(x => handleResponse(x));
}

Struggle with chaining of promises in react application

JavaScript, React - sending multiple simultaneous ajax calls struggling with promises. Basically I want to chain the calls, if one server call completes then only do next call, and collect the successful response of calls from endpoint /pqr made inside makeServerCalls.
import Promise from 'bluebird';
import request from 'superagent';
// sends a post request to server
const servercall2 = (args, response) => {
const promise = new Promise((resolve, reject) => {
const req = request
.post(`${baseUrl}/def`)
.send(args, response)
.setAuthHeaders();
req.endAsync()
.then((res) => resolve(res))
.catch((err) => reject(err));
});
return promise;
};
// sends a post request to server
const servercall1 = (args) => {
const promise = new Promise((resolve, reject) => {
const req = request
.post(`${baseUrl}/abc`)
.send(args)
.setAuthHeaders();
req.endAsync()
.then((res) => resolve({res}))
.catch((err) => reject(err));
});
return promise;
};
// function to send request to cgi server to execute actions from ui
async function makeServerCalls(args, length) {
// convert args to two dimensional array, chunks of given length [[1,2,3], [4,5,6,], [7,8]]
const batchedArgs = args.reduce((rows, key, index) => (index % length === 0 ? rows.push([key])
: rows[rows.length - 1].push(key)) && rows, []);
const responses = [];
for (const batchArgs of batchedArgs) {
responses.push(
// wait for a chunk to complete, before firing the next chunk of calls
await Promise.all(
***// Error, expected to return a value in arrow function???***
batchArgs.map((args) => {
const req = request
.get(`${baseUrl}/pqr`)
.query(args)
// I want to collect response from above req at the end of all calls.
req.endAsync()
.then((response) =>servercall2(args,response))
.then((res) => res);
})
)
);
}
// wait for all calls to finish
return Promise.all(responses);
}
export function execute(args) {
return (dispatch) => {
servercall1(args)
.then(makeServerCalls(args, 3))
.then((responses) => {
const serverresponses = [].concat(...responses);
console.log(serverresponses);
});
};
}
Error: expected to return a value in arrow function. What am I doing wrong here?
Is this a right chaining or it can be optimized?
What happens if some call fails in between?
You can use Async library for this. No need to re-invent the wheel.
There is a waterfall function that takes a list of functions that execute in series. You can pass result of function 1 to function 2 to function 3 and so on. Once complete waterfall executes, you get the result in callback. You can read more about it in the docs in the link above.

async.queue within a promise chain?

I am trying to create an async queue for an array of get requests to an api, i am just unsure how to combine and use the responses. Maybe my implementation is wrong since i am using async.queue inside a promise then function ?
Ultimately i would like to get results from first promise ->
use results of that first promise to create an array of get requests for the async.queue ->
then combine the results of all the get responses. I need to throttle the amount of requests that go out at a time due to API rate limit.
const rp = require("request-promise");
app.get("/", (req,res) => {
let arr = []
rp.get(url)
.then((response) => {
let arrayID = response
let q = async.queue((task, callback) => {
request({
method: "GET",
url: url,
qs: {
id: task.id
}
}, (error, response, body) => {
arr.push(body)
console.log(arr.length)
// successfully gives me the response i want. im trying to push into an array with all of my responses,
// but when i go to next then chain it is gone or if i try to return arr i get an empty []
})
callback()
}, 3)
for(var i = 0; i < arrayID.length; i++){
q.push({ id : arrayID[i]} );
}
q.drain = function() {
console.log('all items have been processed');
}
return arr
})
.then((responseArray) => {
//empty array even though the length inside the queue said other wise, i know its a problem with async and sync actions but is there a way to make the promise chain and async queue play nice?
res.json(responseArray)
})
})
Figured it out, ended up having to wrap it in a promise and resolve the final array in q.drain()
const rp = require("request-promise");
app.get("/", (req,res) => {
rp.get(url)
.then((response) => {
let arrayID = response
return new Promise((resolve, reject) => {
var q = async.queue((task, callback) => {
request({
method: "GET",
url: url,
qs: {
id:task.id,
},
}, (error, response, body) => {
arr.push(body)
callback();
})
}, 2);
q.drain = () => resolve(arr);
q.push(arrayID);
})
})
.then((response) => res.json(response))
.catch((error) => res.json(error))
}
To launch multiple async calls in parallel you can use Promise.all()
To launch multiple async calls sequentially (i.e they depend on each other) you can return each promise and use its result inside a then() function
Code below:
app.get("/", (req,res)
.then(function(firstResult)) {
//You can use result of first promise here
return Promise.all([
//Create array of get request here
//To also return firstResult just add it in the Promise.All array
]);
})
.then(function(allResults){
//You can use results of all the get requests created in the previous then()
})
.catch(function(error){
//Deal with any error that happened
});

Categories

Resources