I have an array, said arr, and I need to iterate on it.
What I tried so far :
arr.forEach((rack) => {
request.post({
headers: {'content-type' : 'application/x-www-form-urlencoded'},
url: 'https://example.com/render',
body: "target=foobar"
}, function(error, response, data){
if (error) {
console.err(error);
} else {
console.log(JSON.stringify(data, null, 4));
}
});
});
I have no output, nor error.
Bonus question: Can I return a value from within the post in the global scope ?
forEach is strictly synchronous and is generally obsolete because it doesn't support generators and async..await while loop statements do. This can be done with async..await and request-promise, which is official promisified counterpart to request:
// within async function
for (const rack of arr) {
try {
const data = await request.post({
headers: {'content-type' : 'application/x-www-form-urlencoded'},
url: 'https://example.com/render',
body: "target=foobar"
});
console.log(JSON.stringify(data, null, 4));
} catch (error) {
console.err(error);
}
}
Since requests don't depend on each other, they could be performed in parallel with Promise.all.
Can I return a value from within the post in the global scope ?
This is a special case of this infamous problem. A value from asynchronous call cannot be consumed in synchronous manner, this extends asynchronous control flow to all call stack, possibly up to entry point:
(async () => {
const result = await asyncFn();
})().catch(console.error)
The first part of your question is because request is asynchronous, you complete the forEach loop and exit before the call is complete.
As for the second part, you can use promises to basically await the result of your query. The code
function getPromise(url) {
return new Promise((resolve, reject) => {
request.post({
headers: {'content-type' : 'application/x-www-form-urlencoded'},
url: 'https://example.com/render',
body: "target=foobar"
}, function(error, response, data) {
if(error) {
reject(error)
} else {
resolve(JSON.stringify(data, null, 4))
}
})
})
}
var promiseArray = arr.map((rack) => getPromise(rack))
Promise.all(promiseArray).then((value) => {
console.log('I am done');
})
Would be what you want. You can assign back to the global scope value (though it's not recommended)
A solution would be to wrap it in a promise:
for rack of arr {
try {
const result = await new Promise((res, rej) => {
request.post({
headers: {'content-type' : 'application/x-www-form-urlencoded'},
url: 'https://example.com/render',
body: "target=foobar"
}, function(error, response, data){
if (error) {
rej(error);
console.err(error);
} else {
res(data);
}
});
});
console.log(JSON.stringify(result, null, 4));
} catch(err) {
console.log(err);
}
}
Related
I'm trying to resolve an array of promises using a timeout to avoid rate limit exceptions on API requests. However, I'm still hitting the rate limit, as the timeout doesn't seem to be working.
Not really sure how to make this work.
router.route("/").put(async (req, res) => {
const { apiKey: access_token, course, assignments } = req.body;
try {
const returnedIds = [];
const promises = await assignments.map((assignment) => {
return axios({
method: "PUT",
url: `https://url.to.api/api/v1/courses/${course}/assignments/${assignment.id}`,
params: {
access_token,
},
data: {
assignment: {
points_possible: assignment.points_possible,
},
},
});
});
const promiseResolution = function() {
Promise.all([...promises]).then((values) => {
values.forEach((_, index) => {
returnedIds.push(assignments[index].id);
});
res.status(201).json({
returnedIds,
});
});
};
setTimeout(promiseResolution, 5000);
} catch (e) {
res.status(401);
}
});
If you just want to put some time between API calls this should do.
router.route("/").put(async (req, res) => {
const { apiKey, course, assignments } = req.body;
try {
const returnedIds = [];
for (const assignment of assignments) {
returnedIds.push(await loadID(apiKey, course, assignment));
await wait(5000);
}
res.status(201).json({ returnedIds })
} catch (e) {
res.status(401);
}
});
function wait(duration) {
return new Promise((resolve) => setTimeout(resolve, duration));
}
function loadID(apiKey, course, assignment) {
// Makes the request, parses out the stuff you want from the response...
}
I would caution against using Promise.all since you probably want to check the result of each request before making the next one. For example, if the third request gets rate limited, you probably shouldn't bother making further requests.
It's because Promise.all will fire all promises at once, so your setTimeout is just set a timeout for all promises not individual promise as well.
You should try to make delay for each promise:
const promises = await assignments.map((assignment) => {
// some delay function
return axios({
method: "PUT",
url: `https://url.to.api/api/v1/courses/${course}/assignments/${assignment.id}`,
params: {
access_token,
},
data: {
assignment: {
points_possible: assignment.points_possible,
},
},
});
});
You can try this: (It's React but you should only focus on fetchData function) and see the logs:
https://codesandbox.io/s/zen-feynman-ql833?file=/src/App.js
I'm not sure but i cant get the life of me to get this to resolve. its always pending. I'm not the best with promises so please help me out.
export async function getQuotes() {
const options = {
headers: {
"x-rapidapi-host": API_URL,
"x-rapidapi-key": API_KEY,
"content-type": "application/x-www-form-urlencoded",
},
};
let res = await axios
.post(API_URL, {}, options)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
return res;
}
and this is how i'm calling it:
const new_data = dataApi.getQuotes();
console.log(new_data);
with the new_data variable, i'd like to access the data that was returned. I keep getting back pending promises instead.
In javascript, async functions return a promise that resolves to their return value. In order to access the data, you have to wrap it in another async function and call await or use .then:
// Either:
async function main() {
const new_data = await dataApi.getQuotes();
console.log(new_data);
}
// or
dataApi.getQuotes().then(new_data => console.log(new_data));
Read more here.
You can wait until the promise has been fulfilled and then process the data as follows:
dataApi.getQuotes()
.then(function(new_data) {
console.log(new_data);
})
.catch(function (error) {
// error handling
});
You should use async/await or promises (.then and .catch) - but not both at the same time. You should also know that an async function always returns a Promise, so you can use it with the then/catch syntax if you want.
// Call with the then/catch syntax
dataApi.getQuotes()
.then((data) => console.log(data))
.catch((err) => console.error(err))
// Is the same that this call with async await
async function main() {
try {
const data = await dataApi.getQuotes();
console.log(data)
} catch (err) {
console.error(err)
}
}
About your function, you're using async/await here. So you may not use then/catch but await in its body.
export async function getQuotes() {
const options = {
headers: {
"x-rapidapi-host": API_URL,
"x-rapidapi-key": API_KEY,
"content-type": "application/x-www-form-urlencoded",
},
};
try {
let res = await axios.post(API_URL, {}, options);
console.log(response);
catch (error) {
console.log(error);
}
return res;
}
Good morning!
I've been struggling to get a specific value returned from my function:
const getFolders = function (PID){
var token = getStoredToken()
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function test(err, response, body){
return body
})
// I NEED THE RETURN VALUE OF THE ABOVE FUNCTION HERE SO I CAN ACCESS WHEN CALLING getFolders()
}
Is this possible? If so, how?
Thanks!
Usually there will be three ways dealing with asynchronous stuff:
callback
promise
async/await
callback:
const getFolders = function(PID, callback) {
var token = getStoredToken()
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function(err, response, body) {
callback(body)
})
}
getFolders(pid, (v) => {
console.log(v)
})
promise:
const getFolders = function(PID, callback) {
return new Promise((resolve, reject) => {
var token = getStoredToken()
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function(err, response, body) {
if (err) {
return reject(err)
}
resolve(body)
})
})
}
getFolders(pid)
.then(v => {
console.log(v)
}).catch(error => {
console.error(error)
})
async/await:
Due to async/await is actually a syntax sugar, the getFolders function is the same as using promise's, the difference is when you call it:
(async function() {
try {
let v = await getFolders(pid)
console.log(v)
} catch(e) {
console.error(e)
}
})()
Not sure if this solve your confusion.
The way you are expecting is wrong, the test function which you have passed to request.get method is a callback function which will execute in asychronous manner , it means whenever your API responds from the server, that callback function will get execute.
So before that you are expecting the response (body) below the request method, which is wrong.
In this case you just have to write some other function to call this get method and in callback function you can easily access that response or just write your code in that test function itself.
like below - :
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, function test(err, response, body){
// instead of returning body
// use the body here only
let result = body;
// your code here
})
Or the other way -:
const getFolders = function (PID){
var token = getStoredToken();
this.get(function(err, response, body){
// do whatever you want with the response now
updateFolder()
})
}
function get(callback){
request.get({
url: 'https://api.procore.com/vapid/folders',
headers: {
Authorization: "Bearer " + token.access_token
},
json: {
company_id: '12594',
project_id: PID
}
}, callback)
}
I am trying to create an async lambda function that makes a http Get call put it's not working correctly and I believe it has to do with it being async. If I remove await/async, and make it synchronous, my function works correctly. I'm not sure what I am doing wrong. Thank you for your help!
exports.handler = async function (event, context) {
await setBattery();
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
async function setBattery() {
'use strict';
const https = require('https');
const options = {
hostname: 'testsite.com',
port: 443,
path: '/proxy/api/setting?EM_OperatingMode=1',
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
};
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', d => {
process.stdout.write(d);
});
});
req.on('error', error => {
console.error(error);
});
req.end();
}
According with MDN Web Docs:
The async function declaration defines an asynchronous function, which
returns an AsyncFunction object. An asynchronous function is a
function which operates asynchronously via the event loop, using an
implicit Promise to return its result. But the syntax and structure of
your code using async functions is much more like using standard
synchronous functions.
So, You need to return a Promise object in order to be handled by your async function (You could also use another promise to handle it), I converted setBattery to a normal function and the return of this function is now a promise that will be handled by your handler (exports.handler). I haven't tested the code but it should work.
exports.handler = async function (event, context) {
await setBattery();
// TODO implement
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
function setBattery() {
'use strict';
const https = require('https');
const options = {
hostname: 'testsite.com',
port: 443,
path: '/proxy/api/setting?EM_OperatingMode=1',
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
};
// Return it as a Promise
return new Promise((resolve, reject) => {
const req = https.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', d => {
process.stdout.write(d);
// If successful
resolve(d);
});
});
req.on('error', error => {
console.error(error);
// If failed
reject(error);
});
req.end();
});
}
I have a method in a class that is supposed to only throw error log messages and then another method in another function that will get this errors and console them (for now).
The problem is that it just throws the error in the console and not in an upper level...
This are the methods from file1.ts:
private sendToApp2(App2Url: string, logObject: object) {
try
{
let uriOptions = {
host: this.hostname,
port: this.port,
path: '/App2',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Api-version': '1'
}
};
this.App2Call(uriOptions, JSON.stringify(logObject));
}
catch (err) {
throw err;
}
}
private App2Call(params: Object, data: string) {
return new Promise((resolve, reject) => {
try {
let req = http.request(params, (response) => {
response.on('data', (data) => {
resolve(data);
});
}).on('error', (err: Error) => {
reject(new Error(err.message));
});
req.write(data);
req.end();
}
catch (err) {
reject(new Error(err.message));
}
});
}
And in the second file, I do this logic to catch the error and console it:
GetErrors() {
try {
// initialize the class object
let getErrors = new FirstClass();
await getErrors.sendToApp2(this.App2Url, this.logObject);
}
catch (err) {
console.log(err);
}
}
It looks like a simple case of having forgotten to use async on the definition of sendToApp2. Since it's not async, there are no errors thrown during sendToApp2 since it returns before the promise from App2Call rejects.
Where you're using sendToApp2 you're using await, which suggests you're expecting it to return a promise, so just add async to the definition:
private async sendToApp2(App2Url: string, logObject: object) {
// ^^^^^
Then the try/catch can handle rejections as errors.
Side note: Right now, there's no point to the try/catch in sendToApp2, since all it does is throw err.
return a promise from the sendToApp2 and deal with the errors when you call the function
private sendToApp2(App2Url: string, logObject: object) {
let uriOptions = {
host: this.hostname,
port: this.port,
path: '/App2',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Api-version': '1'
}
};
return this.App2Call(uriOptions, JSON.stringify(logObject));
}
etErrors.sendToApp2(this.App2Url, this.logObject).then(
function(){},
function(err){ //error callback,or you can use catch();
alert(err);
});