I have an assignment to complete a function that makes API requests using a name. This request will return a json object and this object has a 'height' field that the function should return. I tried but my solution odes not seem to work. Below is my code. Can someone point me in the right direction?
async function getHeight(name) {
let url = "sample url"
https.get(url, (resp) => {
let data = '';
resp.on('data', (chunk) => {
data += chunk;
});
resp.on('end', () => {
let result = (JSON.parse(data));
let result_data = result[data];
if(result_data == null){
return -1
} else{
let cataJson = (JSON.parse(result_data));
return cataJson["height"];
}
});
}).on("error", (err) => {
return -1;
});
}
async function getHeight(name) {
let url = "sample url"
try {
let res = await fetch(url);
return await res.json();
} catch (error) {
console.log(error);
}
}
this code should be some thing like what you need.
you make a get request to the url then you parse the given data to json format then the function returns that data.
You can use fetch with promise like this.
async function getHeight(name) {
return new Promise((resolve, reject)=>{
let url = "sample URL"
fetch(url)
.then(res=>res.json())
.then(data=>{
let result = (JSON.parse(data));
let result_data = result[data];
if(!result_data)resolve(-1)
else resolve((JSON.parse(result_data)).height)
})
.catch(e=>{reject()})
})
}
And to call the function
async function main(){
try{
var height = await getHeight("John");
console.log(height)
}catch(e){
console.log("Error Fetching height")
}
}
main()
const getHeight = async (name) {
let url = "url"
try {
let res = await fetch(url,{
method: "GET"
);
if (res.ok == true) {
return await res.json();
} else {
throw new Error("error")
}
} catch (error) {
console.log(error);
}
}
Related
How to return a Promise from the multiple axios get requests?
I have below code.
async function main() {
const URL_1 = 'abc.com/get1/data1';
const result_1 = await getData(URL_1);
const URL_2 = 'abc.com/get2/data2';
const result_2 = await getData(URL_2);
}
async function getData(dataURI) {
let getURI = dataURI;
const config = {
headers: {
Authorization: `Bearer ${my-token-text}`,
},
};
var finalData = [];
// until we get the next URL keep sending the requests
while (getURI != null) {
try {
const getResult = await axios.get(getURI, config);
if (getResult.status === 200) {
const receivedData = getResult.data.value;
finalData.push(...receivedData);
// check if we have nextLink in the payload
if (Object.prototype.hasOwnProperty.call(getResult.data, 'nextLink')) {
getURI = getResult.data.nextLink;
} else {
getURI = null;
return finalData;
}
}
} catch (err) {
break;
}
}
return null;
}
What I am trying to achieve is:
async function main() {
const URL_1 = 'abc.com/get1/data1';
const result_1 = getData(URL_1);
promisesArray.push(result_1);
const URL_2 = 'abc.com/get2/data2';
const result_2 = getData(URL_2);
promisesArray.push(result_2);
await Promise.allSettled(promisesArray).then((results) => {
console.log('Promise All Done: ', results);
});
}
This why I can perform all the requests in parallel.
But when I update the function getData(dataURI) to return return new Promise then I get error for await axios.
async function getData(dataURI) {
return new Promise((resolve, reject) => {
// Same code as above
});
}
I get error:
SyntaxError: await is only valid in async function
As Promise is not async I cannot await in the Promise.
Have you tried:
return new Promise(async (resolve, reject) => {
// Same code as above
});
Task is to return data from getData function to main function.
function getData(){
const https = require('https')
const url = "https://...../api/movies";
https.get(url, res => {
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
data = JSON.parse(data);
console.log(data);
//How can I return this data to main function?
})
}).on('error', err => {
console.log(err.message);
})
}
function main(){
console.log(getData());
}
I am not able to access data or print data in main function
I think you already have the answer in your comment '//How can I return this data to main function?'.
...
res.on('end', () => {
data = JSON.parse(data);
console.log(data);
return data;
})
...
So the return-value of your getData-function should now be the parsed json-data and be accessible in the main-function.
I would create a variable to save value which you want to return
const https = require('https')
function getData() {
let ret = null;
const url = "https://...../api/movies";
https.get(url, res => {
let data = '';
res.on('data', chunk => {
data += chunk;
});
res.on('end', () => {
ret = JSON.parse(data);
})
}).on('error', err => {
console.log(err.message);
})
return ret;
}
function main() {
console.log(getData());
}
What you could do is to wrap your request into a Promise. You would want to return this promise and and wait for it to be fullfilled:
function getData() {
const url = "https://...../api/movies";
return new Promise((resolve, reject) => {
const req = https.get(url, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
resolve(JSON.parse(data));
});
});
req.on('error', (err) => {
reject(err);
});
req.end();
});
}
function main() {
getData().then(data => {
console.log("Response:", data);
}, error => {
console.error(error);
});
}
Moreover you can make your main function async and use await to get the response:
async function main() {
const data = await getData();
console.log(data);
}
// Start an IIFE to use `await` at the top level. See: https://stackoverflow.com/a/14220323/7661119
(async () => {
await main()
})();
I have a function who send a message to the server to get the answer and if the answer is true I want my application to send an error to the user. The problem is that I can't manage to await the callback in the Fetch function I wrote.
This is the function who send the question to the server.
async donglePaired(){
if (Platform.OS !=='ios'){
var pairedDevices = await BluetoothScanner.getPairedDevices();
console.log('Sending........');
let data={
data:pairedDevices,
};
new Api().fetch("bluetoothCheck",{devices:JSON.stringify(data),userid:this.state.probe.UID},(result) => {
if (!result.err) return false;
console.log("Dongle already paired");
return true;
//logNetworkState
});
}
}
This is the Api.fetch function i wrote
fetch(action,data,cb){
let url=Config.URL+"?version="+Config.VERSION+"&action="+action;
let post="";
let formData=new FormData();
for(let k in data) formData.append(k,data[k]);
for(let k in data) post+="&"+k+"="+encodeURIComponent(data[k]).replace(/%20/g,'+');
console.log(url+post);
console.log(url);
if (data.batch) console.log(data.batch);
let sending=true;
fetch(url,{
method: 'post',
body: formData
})
.then(function(response){
if (true) return response.json();
let txt=response.text();
console.log(txt);
return JSON.parse(txt);
})
.then(
(result)=>{
if (!sending) return;
sending=false;
console.log(JSON.stringify(result));
if (cb) cb(result);
},
(error)=>{
if (!sending) return;
sending=false;
console.log("fetch error");
console.log(error);
if (cb) cb();
}
);
setTimeout(()=>{
console.log("http timeout")
if (!sending) return console.log("nothing to abort");
if (cb) cb();
},Config.HTTP_TIMEOUT*1000)
}
}
And this is my main code where I wait for the first function donglePaired, and if donglePaired return true I send an error to the user.
let donglePaired = await this.props.app.donglePaired();
if (donglePaired) return this.props.app.setError("ERR_DONGLE");
The problem is that the program doesnt wait for donglePaired, despite of the await
your code here is inappropriate
let donglePaired = await this.props.app.donglePaired();
if (donglePaired) return this.props.app.setError("ERR_DONGLE");
Async function cannot return value normally unless it is a Promise
See my simple demo below!
async function test() {
const result = await asyncRequest()
return result
}
function asyncRequest() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
}, 2000)
})
}
test().then((data) => {
console.log(data)
})
The snippets should give you an idea how to await the callback
Sending to the API
async function remove_configuration(filename) {
const data = { filename };
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
};
await fetch('/delete', options);
}
Just Retrieving Data
async function display() {
let response = await fetch('/get-available-configurations')
let data = await response.json(); // JSON or Text what do you prefer
// do something with data
}
You could return an Promise.race() with your timeout function.
fetch(action, data, cb) {
let url = Config.URL + "?version=" + Config.VERSION + "&action=" + action;
let post = "";
let formData = new FormData();
for (let k in data) formData.append(k, data[k]);
for (let k in data)
post += "&" + k + "=" + encodeURIComponent(data[k]).replace(/%20/g, "+");
console.log(url + post);
console.log(url);
if (data.batch) console.log(data.batch);
let sending = true;
return Promise.race([
fetch(url, {
method: "post",
body: formData
})
.then(res => res.json())
.then(result => {
if (!sending) return;
sending = false;
return result;
}),
sleep(Config.HTTP_TIMEOUT * 1000)
]);
}
const sleep = ms => new Promise((_, rej) => setTimeout(rej("TIMEOUT"), ms));
It either returns you the value or it rejects with TIMEOUT or it rejects with an error from fetch
And donglePaired looks like this then. I have wrapped it with an try / catch
async donglePaired() {
if (Platform.OS !== "ios") {
var pairedDevices = await BluetoothScanner.getPairedDevices();
console.log("Sending........");
let data = {
data: pairedDevices
};
try {
let result = await new Api().fetch("bluetoothCheck", {
devices: JSON.stringify(data),
userid: this.state.probe.UID
});
if (!result.err) return false;
console.log("Dongle already paired");
return true;
//logNetworkState
} catch (err) {
console.log(err);
}
}
}
One possibility is to drop the async and change it to this:
donglePaired() {
return new Promise( function(resolve, reject) {
if (Platform.OS !=='ios'){
var pairedDevices = await BluetoothScanner.getPairedDevices();
console.log('Sending........');
let data={
data:pairedDevices,
};
new Api().fetch("bluetoothCheck",{devices:JSON.stringify(data),userid:this.state.probe.UID},(result) => {
if (!result.err) reject(false);
console.log("Dongle already paired");
resolve(true);
//logNetworkState
});
}
reject(false);
});
}
And:
this.props.app.donglePaired().then( (response) => {
// do something here, this will only run if the response is true
});
I know that old school for loop works in the traditional way - that it waits for the await to finish getting results.
But in my use case, I need to read a file from local/s3 and process it line by line, and for each line I need to call an External API.
Generally I use await inside the loop because all are running inside a lambda and I don't want to use all memory for running it parallelly.
Here I am reading the file using a stream.on() method, and in order to use await inside that, I need to add async in read method, like so:
stream.on('data',async () =>{
while(data=stream.read()!==null){
console.log('line');
const requests = getRequests(); // sync code,no pblms
for(let i=0;i<requests.length;i++){
const result = await apiCall(request[i);
console.log('result from api')
const finalResult = await anotherapiCall(result.data);
}
}
});
This is working but order in which the lines are processed is not guaranteed. I need all in a sync manner. Any help?
Complete Code
async function processSOIFileLocal (options, params) {
console.log('Process SOI file');
const readStream = byline.createStream(fs.createReadStream(key));
readStream.setEncoding('utf8');
const pattern = /^UHL\s|^UTL\s/;
const regExp = new RegExp(pattern);
readStream.on('readable', () => {
let line;
while (null !== (line = readStream.read())) {
if (!regExp.test(line.toString())) {
totalRecordsCount++;
dataObject = soiParser(line);
const { id } = dataObject;
const XMLRequests = createLoSTRequestXML(
options,
{ mapping: event.mapping, row: dataObject }
);
console.log('Read line');
console.log(id);
try {
for (let i = 0;i < XMLRequests.length;i++) {
totalRequestsCount++;
console.log('Sending request');
const response = await sendLoSTRequest(
options,
{ data: XMLRequests[i],
url: LOST_URL }
);
console.log("got response");
const responseObj = await xml2js.
parseStringPromise(response.data);
if (Object.keys(responseObj).indexOf('errors') !== -1) {
fs.writeFileSync(`${ERR_DIR}/${generateKey()}-${id}.xml`, response.data);
failedRequestsCount++;
} else {
successRequestsCount++;
console.log('Response from the Lost Server');
console.log(response[i].data);
}
}
} catch (err) {
console.log(err);
}
}
}
})
.on('end', () => {
console.log('file processed');
console.log(`
************************************************
Total Records Processed:${totalRecordsCount}
Total Requests Sent: ${totalRequestsCount}
Success Requests: ${successRequestsCount}
Failed Requests: ${failedRequestsCount}
************************************************
`);
});
}
async function sendLoSTRequest (options, params) {
const { axios } = options;
const { url, data } = params;
if (url) {
return axios.post(url, data);
// eslint-disable-next-line no-else-return
} else {
console.log('URL is not found');
return null;
}
}
Code needs to flow like so:
read a line in a sync way
process the line and transform the line into an array of two members
for every member call API and do stuff
once line is complete, look for another line, all done in order
UPDATE: Got a workaround..but it fires stream.end() without waiting stream to finish read
async function processSOIFileLocal (options, params) {
console.log('Process SOI file');
const { ERR_DIR, fs, xml2js, LOST_URL, byline, event } = options;
const { key } = params;
const responseObject = {};
let totalRecordsCount = 0;
let totalRequestsCount = 0;
let failedRequestsCount = 0;
let successRequestsCount = 0;
let dataObject = {};
const queue = (() => {
let q = Promise.resolve();
return fn => (q = q.then(fn));
})();
const readStream = byline.createStream(fs.createReadStream(key));
readStream.setEncoding('utf8');
const pattern = /^UHL\s|^UTL\s/;
const regExp = new RegExp(pattern);
readStream.on('readable', () => {
let line;
while (null !== (line = readStream.read())) {
if (!regExp.test(line.toString())) {
totalRecordsCount++;
dataObject = soiParser(line);
const { id } = dataObject;
const XMLRequests = createLoSTRequestXML(
options,
{ mapping: event.mapping, row: dataObject }
);
// eslint-disable-next-line no-loop-func
queue(async () => {
try {
for (let i = 0;i < XMLRequests.length;i++) {
console.log('Sending request');
console.log(id);
totalRequestsCount++;
const response = await sendLoSTRequest(
options,
{ data: XMLRequests[i],
url: LOST_URL }
);
console.log('got response');
const responseObj = await xml2js.
parseStringPromise(response.data);
if (Object.keys(responseObj).indexOf('errors') !== -1) {
// console.log('Response have the error:');
// await handleError(options, { err: responseObj, id });
failedRequestsCount++;
fs.writeFileSync(`${ERR_DIR}/${generateKey()}-${id}.xml`, response.data);
} else {
console.log('Response from the Lost Server');
console.log(response[i].data);
successRequestsCount++;
}
}
} catch (err) {
console.log(err);
}
});
}
}
})
.on('end', () => {
console.log('file processed');
console.log(`
************************************************
Total Records Processed:${totalRecordsCount}
Total Requests Sent: ${totalRequestsCount}
Success Requests: ${successRequestsCount}
Failed Requests: ${failedRequestsCount}
************************************************
`);
Object.assign(responseObject, {
failedRequestsCount,
successRequestsCount,
totalRecordsCount,
totalRequestsCount
});
});
}
Thank You
The sample code at the top of your question could be rewritten like
const queue = (() => {
let q = Promise.resolve();
return (fn) => (q = q.then(fn));
})();
stream.on('data', async() => {
while (data = stream.read() !== null) {
console.log('line');
const requests = getRequests(); // sync code,no pblms
queue(async () => {
for (let i = 0; i < requests.length; i++) {
const result = await apiCall(request[i]);
console.log('result from api');
const finalResult = await anotherapiCall(result.data);
}
});
}
});
Hopefully that will be useful for the complete code
If anyone want a solution for synchronisely process the file, ie, linebyline read and execute some Async call, it's recommended to use inbuilt stream transform. There we can create a transform function and return a callback when finishes.
That's will help of any one face this issues.
Through2 is a small npm library that also can be used for the same.
I am trying to consume an API which returns structure below.
{
data: [{...},{...},{...},{...},...],
nextUrl: "url_goes_here";
}
The pages end when the nextUrl is null. I want to collect all the elements in data into one array while going through the paginated responses. I tried the following code segment.
const getUserData = async (url) => {
const result = await fetch(url)
.then(res => res.json())
.then(res => {
dataList= [...dataList, ...res.data];
console.log(res.data)
if (res.nextUrl !== null) {
getUserData(res.nextUrl);
} else {
console.log("else ", dataList);
}
})
.catch(err => {});
return result;
}
The console.log can print the result. but I want to get all the data to get into a variable that can be used for further processing later.
Your approach using recursion isn't bad at all, but you're not returning the chunk of data (there's no value returned by your second .then handler). (You're also falling into the fetch pitfall of not checking ok. It's a flaw IMHO in the fetch API design.)
There's no need for recursion though. I'd be tempted to just use a loop:
const getUserData = async (url) => {
const result = [];
while (url) {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
const {data, nextUrl} = await response.json();
result.push(...data);
url = nextUrl;
}
return result;
};
But if you want to use recursion:
const getUserData = async (url) => {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
const {data, nextUrl} = await response.json();
if (nextUrl) {
data.push(...await getUserData(nextUrl));
}
return data;
};
or
const getUserData = async (url, result = null) => {
const response = await fetch(url);
if (!response.ok) {
throw new Error("HTTP error " + response.status);
}
const {data, nextUrl} = await response.json();
if (!result) {
result = data;
} else {
result.push(...data);
}
if (nextUrl) {
result = await getUserData(nextUrl, result);
}
return result;
};