using a fetch inside another fetch in javascript - javascript

I want to get an api and after that call another one. Is it wisely using a code like this in javascript?
fetch(url, {
method: 'get',
}).then(function(response) {
response.json().then(function(data) {
fetch(anotherUrl).then(function(response) {
return response.json();
}).catch(function() {
console.log("Booo");
});
});
})
.catch(function(error) {
console.log('Request failed', error)
});

Fetch returns a promise, and you can chain multiple promises, and use the result of the 1st request in the 2nd request, and so on.
This example uses the SpaceX API to get the info of the latest launch, find the rocket's id, and fetch the rocket's info.
const url = 'https://api.spacexdata.com/v4';
const result = fetch(`${url}/launches/latest`, { method: 'get' })
.then(response => response.json()) // pass the data as promise to next then block
.then(data => {
const rocketId = data.rocket;
console.log(rocketId, '\n');
return fetch(`${url}/rockets/${rocketId}`); // make a 2nd request and return a promise
})
.then(response => response.json())
.catch(err => {
console.error('Request failed', err)
})
// I'm using the result const to show that you can continue to extend the chain from the returned promise
result.then(r => {
console.log(r.first_stage); // 2nd request result first_stage property
});
.as-console-wrapper { max-height: 100% !important; top: 0; }

There is not an issue with nesting fetch() calls. It depends on what you are trying to achieve by nesting the calls.
You can alternatively use .then() to chain the calls. See also How to structure nested Promises
fetch(url)
.then(function(response) {
return response.json()
})
.then(function(data) {
// do stuff with `data`, call second `fetch`
return fetch(data.anotherUrl)
})
.then(function(response) {
return response.json();
})
.then(function(data) {
// do stuff with `data`
})
.catch(function(error) {
console.log('Requestfailed', error)
});

This is a common question people get tripped up by when starting with Promises, myself included when I began. However, first...
It's great you're trying to use the new Fetch API, but if I were you I would use a XMLHttpRequest implementation for now, like jQuery AJAX or Backbone's overridden implementation of jQuery's .ajax(), if you're already using these libraries. The reason is because the Fetch API is still so new, and therefore experimental at this stage.
With that said, people definitely do use it, but I won't in my own production code until it's out of "experimental" status.
If you decide to continue using fetch, there is a polyfill available. NOTE: you have to jump through extra hoops to get error handling to work properly, and to receive cookies from the server. If you're already loading jQuery, or using Backbone, just stick with those for now; not completely dreadful, anyway.
Now onto code:
You want a flat structure, else you're missing the point of Promises. It's not wise to nest promises, necessarily, because Promises solve what nested async callbacks (callback hell) could not.
You will save yourself time and energy, and produce less buggy code by simply using a more readable code structure. It's not everything, but it's part of the game, so to speak.
Promises are about making asynchronous code retain most of the lost properties
of synchronous code such as flat indentation and one exception
channel.
-- Petka Antonov (Bluebird Promise Library)
// run async #1
asyncGetFn()
// first 'then' - execute more async code as an arg, or just accept results
// and do some other ops
.then(response => {
// ...operate on response data...or pass data onto next promise, if needed
})
// run async #2
.then(asyncGetAnotherFn)
.then(response => {
// ...operate on response data...or pass data onto next promise, if needed
})
// flat promise chain, followed by 'catch'
// this is sexy error handling for every 'then' above
.catch(err => {
console.error('Request failed', err)
// ...raise exeption...
// ... or, retry promise...
})

I didn't saw an answer with the syntactic sugar of async/await, so I'm posting my answer.
Another way to fetch "inside" another fetch in javascript is like -
try {
const response = await fetch(url, {method: 'get'});
const data = response.json();
//use the data...
const anotherResponse = await fetch(url, {method: 'get'});
const anotherdata = anotherResponse.json();
//use the anotherdata...
} catch (error) {
console.log('Request failed', error) ;
}
So actually you call url after url one by one.
This code will work inside async context.

I would use either an array of fetches instead or an array of urls, both in the order you would like to execute them. Then use reduce to execute them in sequence. This way it is much more scalable.
const urls = [
'https://api.spacexdata.com/v4/launches/latest',
'https://api.spacexdata.com/v4/launches/latest',
'https://api.spacexdata.com/v4/launches/latest'
];
// handle the fetch logic
// and handle errors
const handleFetch = async (url) => {
const resp = await fetch(url).catch(console.error);
return resp.json()
}
// reduce fetches, receives the response
// of the previous, log it (and maybe use it as input)
const reduceFetch = async (acc, curr) => {
const prev = await acc;
console.log('previous call:', prev);
return handleFetch(curr);
}
const pipeFetch = async urls => urls.reduce(reduceFetch, Promise.resolve(''));
pipeFetch(urls).then(console.log);

Is it wisely using a code like this in javascript?
Yes. Your code is fine.
Except that after the second request,
fetch(anotherUrl).then(function(response) {,
I would replace return response.json();
with response.json().then(function(data2) { – just as after the
first request.
The variable data2 will then contain the response body of the inner
URL request, as desired.
This means that – whatever you want to do with data2, you must do it
inside this second callback (since you don't return a promise.)
Also, a few more printouts will help to understand what is happening.
1. The original code – slightly modified
After making those changes, here is a Stack Snippet containing your
code:
1
const url = 'https://jsonplaceholder.typicode.com/todos/1';
const anotherUrl = 'https://jsonplaceholder.typicode.com/todos/4';
fetch(url, {
method: 'get'
}).then(function (response) {
response.json().then(function (data) {
console.log('Response body of outer "url":');
console.log(JSON.stringify(data) + '\n\n');
fetch(anotherUrl).then(function (response) {
response.json().then(function (data2) {
console.log('Response body of inner "anotherUrl":');
console.log(JSON.stringify(data2) + '\n\n');
});
}).catch(function () {
console.log('Booo');
});
});
})
.catch(function (error) {
console.log('Request failed', error);
});
.as-console-wrapper { max-height: 100% !important; top: 0; }
which is fine really, although the fat arrow style is more common
these days for defining a function.
2. The code refactored
Here is a refactored version of your code.
It has an inner chained/nested request – fetch(urlInner) – that
depends on data retrieved from a previous/outer request: fetch (urlOuter).
By returning the promises of both the outer and the inner URL fetches,
it is possible to access/resolve the promised result later in the code:
2
const urlOuter = 'https://jsonplaceholder.typicode.com/todos/1';
let urlInner = '';
const resultPromise = fetch(urlOuter)
.then(responseO => responseO.json())
.then(responseBodyO => {
console.log('The response body of the outer request:');
console.log(JSON.stringify(responseBodyO) + '\n\n');
const neededValue = responseBodyO.id + 3;
urlInner = 'https://jsonplaceholder.typicode.com/todos/' + neededValue;
console.log('neededValue=' + neededValue + ', URL=' + urlInner);
return fetch(urlInner)
.then(responseI => responseI.json())
.then(responseBodyI => {
console.log('The response body of the inner/nested request:');
console.log(JSON.stringify(responseBodyI) + '\n\n');
return responseBodyI;
}).catch(err => {
console.error('Failed to fetch - ' + urlInner);
console.error(err);
});
}).catch(err => {
console.error('Failed to fetch - ' + urlOuter);
console.error(err);
});
resultPromise.then(jsonResult => {
console.log('Result - the title is "' + jsonResult.title + '".');
});
.as-console-wrapper { max-height: 100% !important; top: 0; }
Note that no indentation is deeper than eight spaces.
3. Advantages of this code style
This is clearly a nested style of writing the code – meaning that the
chained request fetch(urlInner) is indented and made inside the
callback of the first request fetch(urlOuter).
Yet, the indentation tree is reasonable, and this style resonates well
with my intuition about chaining requests. – But more importantly,
this style makes it possible to write error messages that pinpoint
which URL failed.
Run the snippet below to see how the error message tells that it is the
inner/second URL that causes the error:
const urlOuter = 'https://jsonplaceholder.typicode.com/todos/1';
let urlInner = '';
const resultPromise = fetch(urlOuter)
.then(responseO => responseO.json())
.then(responseBodyO => {
console.log('The response body of the outer request:');
console.log(JSON.stringify(responseBodyO) + '\n\n');
const neededValue = responseBodyO.id + 3;
urlInner = 'https://VERY-BAD-URL.typicode.com/todos/' + neededValue;
console.log('neededValue=' + neededValue + ', URL=' + urlInner);
return fetch(urlInner)
.then(responseI => responseI.json())
.then(responseBodyI => {
console.log('The response body of the inner/nested request:');
console.log(JSON.stringify(responseBodyI) + '\n\n');
return responseBodyI;
}).catch(err => {
console.error('Failed to fetch - ' + urlInner);
console.error(err);
});
}).catch(err => {
console.error('Failed to fetch - ' + urlOuter);
console.error(err);
});
resultPromise.then(jsonResult => {
console.log('Result - the title is "' + jsonResult.title + '".');
});
.as-console-wrapper { max-height: 100% !important; top: 0; }
4. Flattening all occurrences of .then()?
Inspired by others, you may be tempted to flatten all occurrences of
.then(), like below.
I would advise against doing this – or at least think twice before
doing it. Why?
In the absence of errors, it doesn't matter.
If there are errors, such a style will force less distinct error
messages:
const urlOuter = 'https://jsonplaceholder.typicode.com/todos/1';
let urlInner = '';
const resultPromise = fetch(urlOuter)
.then(responseO => responseO.json())
.then(responseBodyO => {
console.log('The response body of the outer request:');
console.log(JSON.stringify(responseBodyO) + '\n\n');
const neededValue = responseBodyO.id + 3;
urlInner = 'https://VERY-BAD-URL.typicode.com/todos/' + neededValue;
console.log('neededValue=' + neededValue + ', URL=' + urlInner);
return fetch(urlInner);
})
.then(responseI => responseI.json())
.then(responseBodyI => {
console.log('The response body of the inner/nested request:');
console.log(JSON.stringify(responseBodyI) + '\n\n');
return responseBodyI;
}).catch(err => {
console.error('Failed to fetch one or more of these URLs:');
console.log(urlOuter);
console.log(urlInner);
console.log(err);
});
.as-console-wrapper { max-height: 100% !important; top: 0; }
The code is nicely flat, but the error caught at the end cannot decide
which of the URL requests that failed.
1 All snippets of this answer comply with the
JavaScript Semistandard Style.
2 About line 11 – return fetch(urlInner) – it is
very easy to forget to return the fetch.
(I once forgot it even after writing this answer.)
If you do forget it, resultPromise will not contain any promise at
all.
The last three lines in the snippet will then fail – they will output
nothing.
The result fails completely!

just some ways of doing it.
1, using async -await
app.get("/getemployeedetails/:id", async (req, res) => {
const id = req.params.id;
const employeeUrl = "http://localhost:3000/employee/" + id;
try {
const response = await fetch(employeeUrl);
const employee = await response.json();
const projectUrl = "http://localhost:3000/project/" + employee.project_id;
const response1 = await fetch(projectUrl);
const project = await response1.json();
const result = {
...employee,
...project,
};
res.send(result);
} catch (error) {
console.log("getData: ", error);
}
});
2, chaining of then
app.get("/getemployeedetails/:id", (req, res) => {
const id = req.params.id;
const employeeUrl = "http://localhost:3000/employee/" + id;
let employeeResponse = null;
fetch(employeeUrl)
.then((employee) => employee.json())
.then((resp) => {
employeeResponse = resp
const projectUrl =
"http://localhost:3000/project/" + employeeResponse.project_id;
return fetch(projectUrl);
})
.then((project) => project.json())
.then((projectResponse) => {
const result = {
...employeeResponse,
...projectResponse,
};
res.send(result);
})
.catch((err) => console.log(err));
});
3, chaining in a better way
app.get("/getemployeedetails/:id", (req, res) => {
const id = req.params.id;
getEmployeeResponse(id).then((employeeResponse) => {
getProjectResponse(employeeResponse.project_id)
.then((projectResponse) => {
const result = {
...employeeResponse,
...projectResponse,
};
res.send(result);
})
.catch((err) => console.log(err));
});
});
function getEmployeeResponse(id) {
return new Promise((resolve, reject) => {
const employeeUrl = "http://localhost:3000/employee/" + id;
fetch(employeeUrl)
.then((employee) => employee.json())
.then((resp) => resolve(resp))
.catch((err) => reject(err));
});
}
function getProjectResponse(id) {
return new Promise((resolve, reject) => {
const projectUrl = "http://localhost:3000/project/" + id;
fetch(projectUrl)
.then((project) => project.json())
.then((resp) => resolve(resp))
.catch((err) => reject(err));
});
}
you decide.

I suggest using axios, is much better and you don't have to deal with JSON format. Also, the code looks cleaner and is easier to understand.
axios.get(firstUrl).then((resp1) => {
// Handle success of first api
axios.get(secondUrl).then((resp2) => {
return resp2.data
}).catch((error) => { /* Handle error of second api */ });
}).catch((error) => { /* Handle error of first api */ });
Blockquote from LogRocket.com:
As with Fetch, Axios is promise-based. However, it provides a more
powerful and flexible feature set.
Advantages of using Axios over the native Fetch API include:
Request and response interception
Streamlined error handling
Protection against XSRF
Support for upload progress
Response timeout
The ability to cancel requests
Support for older browsers
Automatic JSON data transformation

Related

Not able to get results when running HTTPS GET requests on an array of data

I am using the code given below to run multiple https GET request for Wikipedia API.
app.get("/data_results", (req, res) => {
const articlesData = names.map(nameObj => {
let name = nameObj.name;
let articleExtract = "";
let contentURL =
`https://en.wikipedia.org/w/api.php?
action=query&titles=${name}&prop=extracts&format=json&exintro=1&explaintext=false&origin=*`;
// Getting article content
https.get(contentURL, response => {
response.on("data", async data => {
const wikiArticle = JSON.parse(data);
// Mapping the keys of the JSON data of query to its values.
articleExtract = await Object.keys(wikiArticle.query.pages).map(key => wikiArticle.query.pages[key])[0].extract;
nameObj.article = articleExtract.substring(0,350);
})
});
return nameObj;
});
res.send(articlesData);});
This is my names array
[
{ name: 'Indus%20Valley%20Civilisation' },
{ name: 'Ramayana' },
{ name: 'Mahabharata' },
{ name: 'Gautama%20Buddha' }
]
My aim :-
Run the HTTPS GET request for every value in the names array sequentially.
Add the article extract with its name and save in it in an objects array.
You could also suggest me better ways of doing this.
Try this solution. it creates an object like
{
Mahabharata: response
}
The code
const getValues = async () => {
const name_array = [
{name: 'Indus%20Valley%20Civilisation'},
{name: 'Ramayana'},
{name: 'Mahabharata'},
{name: 'Gautama%20Buddha'},
];
const namePromises = name_array.map(value =>
fetch('baseurl' + value.name).then(res => res.json()),
);
const response = await Promise.all(namePromises);
return response.reduce((obj, responseResults, index) => {
obj[name_array[index].name] = responseResults;
}, {});
};
I would suggest you use fetch and promise. all. Map over your array of names and create promises as input. and then return the array of resolved promises.
something like this
const namePromises = [
{ name: 'Indus%20Valley%20Civilisation' },
{ name: 'Ramayana' },
{ name: 'Mahabharata' },
{ name: 'Gautama%20Buddha' }
].map((value) => fetch(baseurl + value.name).then(res=>res.json()));
Promise.all(namePromises).then((response) => {
console.log(response);
});
Problems:
So, you have several things going on here.
http.get() is non-blocking and asynchronous. That means that the rest of your code continues to run while your multiple http.get() operations are running. That means you end up calling res.send(articlesData); before any of the http requests are complete.
.map() is not promise-aware or asynchronous-aware so it will not wait its loop for any asynchronous operation. As such, you're actually running ALL your http requests in parallel (they are all in-flight at the same time). The .map() loop starts them all and then they all finish some time later.
The data event for http.get() is not guaranteed to contain all your data or even a whole piece of data. It may just be a partial chunk of data. So, while your code may seem to get a complete response now, different network or server conditions could change all that and reading the result of your http requests may stop functioning correctly.
You don't show where names comes from in your request handler. If it's a statically declared array of objects declared in a higher-scoped variable, then this code has a problem that you're trying to modify the objects in that array in a request handler and multiple requests to this request handler can be conflicting with one another.
You don't show any error handling for errors in your http requests.
You're using await in a place it doesn't belong. await only does something useful when you are awaiting a promise.
Solutions:
Managing asynchronous operations in Javascript, particularly when you have more than one to coordinate) is a whole lot easier when using promises instead of the plain callback that http.get() uses. And, it's a whole lot easier to use promises when you use an http request interface that already supports promises. My goto library for built-in promise support for http requests in nodejs is got(). There are many good choices shown here.
You can use got() and promises to control the asynchronous flow and error handling like this:
const got = require('got');
app.get("/data_results", (req, res) => {
Promise.all(names.map(nameObj => {
let name = nameObj.name;
// create separate result object
let result = { name };
let contentURL =
`https://en.wikipedia.org/w/api.php?
action=query&titles=${name}&prop=extracts&format=json&exintro=1&explaintext=false&origin=*`;
return got(contentURL).json().then(wikiArticle => {
// Mapping the keys of the JSON data of query to its values.
let articleExtract = Object.keys(wikiArticle.query.pages).map(key => wikiArticle.query.pages[key])[0].extract;
result.article = articleExtract.substring(0, 350);
return result;
});
})).then(articlesData => {
res.send(articlesData);
}).catch(err => {
console.log(err);
res.sendStatus(500);
});
});
This code attempts to fix all six of the above mentioned problems while still running all your http requests in parallel.
If your names array was large, running all these requests in parallel may consume too many resources, either locally or on the target server. Or, it may run into rate limiting on the target server. If that was an issue, you can run sequence the http requests to run them one at a time like this:
const got = require('got');
app.get("/data_results", async (req, res) => {
try {
let results = [];
for (let nameObj of names) {
let name = nameObj.name;
let result = { name };
let contentURL =
`https://en.wikipedia.org/w/api.php?
action=query&titles=${name}&prop=extracts&format=json&exintro=1&explaintext=false&origin=*`;
const wikiArticle = await got(contentURL).json();
let articleExtract = Object.keys(wikiArticle.query.pages).map(key => wikiArticle.query.pages[key])[0].extract;
result.article = articleExtract.substring(0, 350);
results.push(result);
}
res.send(results);
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});
If you really want to stick with https.get(), then you can "promisify" it and use it in place of got():
function myGet(url, options = {}) {
return new Promise((resolve, reject) => {
https.get(url, options, (res) => {
res.setEncoding('utf8');
let rawData = '';
res.on('data', (chunk) => { rawData += chunk; });
res.on('end', () => {
try {
const parsedData = JSON.parse(rawData);
resolve(parsedData);
} catch (e) {
reject(e);
}
});
}).on('error', reject);
});
}
app.get("/data_results", async (req, res) => {
try {
let results = [];
for (let nameObj of names) {
let name = nameObj.name;
let result = { name };
let contentURL =
`https://en.wikipedia.org/w/api.php?
action=query&titles=${name}&prop=extracts&format=json&exintro=1&explaintext=false&origin=*`;
const wikiArticle = await myGet(contentURL);
let articleExtract = Object.keys(wikiArticle.query.pages).map(key => wikiArticle.query.pages[key])[0].extract;
result.article = articleExtract.substring(0, 350);
results.push(result);
}
res.send(results);
} catch (e) {
console.log(e);
res.sendStatus(500);
}
});

What would be a proper way of returning this promise?

I'm new to coding and Promises are somewhat of an abstract concept for me. I know that in order to return a Promise you must use .then and .catch , but I'm unsure how exactly they should be used in the following code.
/**
* Make an HTTPS request to the MTA GTFS API for a given feed.
* #param {String} baseUrl - base URL for MTA GTFS API
* #param {String} feedId - identifier for which realtime feed
* #param {String} apiKey - key for MTA GTFS API
* #return {<Object>} - Promise of parsed feed.
*/
function makeRequest(baseUrl, feedId, apiKey) {
const feedUrl = baseUrl + feedId;
return new Promise((resolve, reject) => {
const req = https.request(feedUrl,
{ headers: { 'x-api-key': apiKey } },
(res) => {
if (res.statusCode < 200 || res.statusCode >= 300) {
return reject(new Error('statusCode=' + res.statusCode));
}
var data;
data = [];
res.on('data', (chunk) => {
return data.push(chunk);
});
return res.on('end', function() {
var msg;
data = Buffer.concat(data);
try {
msg = nstrDecoder.decode(data);
} catch (err) {
try {
console.log(err.message);
msg = nsssDecoder.decode(data);
} catch (err) {
console.log(err.message);
msg = "";
}
}
resolve(msg);
});
}
);
req.on('error', (e) => {
reject(e.message);
});
req.end();
});
}
return console.log(makeRequest(baseUrl, feedId, apiKey));
After running this code I get a message saying the Promise is pending. Thoughts??
When calling a function that returns a promise, you're going to get the promise in a pending state being it hasn't resolve or rejected yet. You have two options.
Option one.
node 10+ you can use async await.
async function main(){
const res = await makeRequest(baseUrl, feedId, apiKey)
return res
}
Pre node 10
makeRequest(baseUrl, feedId, apiKey)
.then(data => {
console.log(data)
})
.catch(err => {
console.log(err)
})
Essentially if you have to wait for the data, you have to have the code that relies on that data after the await, or in the .then block.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
Your makeRequest() function returns a promise. To use that promise, you use .then() or await to get the resolved value from the promise.
Using .then():
makeRequest(baseUrl, feedId, apiKey).then(result => {
console.log(result);
}).catch(err => {
console.log(err);
});
or using await:
async function someFunction() {
try {
let result = await makeRequest(baseUrl, feedId, apiKey);
console.log(result);
} catch(e) {
console.log(err);
}
}
FYI, a lot of what makeRequest() is doing can be done simpler using an http request library that already supports promises. There is a list of competent libraries to choose from here.
My favorite is got(), but you can review the list to decide which one you like. The main advantages are that they already collect the whole response for you, already support promises, support a wide variety of authentication schemes with built-in logic and can often decode or parse the response for you - all things which https.request() does not know how to do by itself.

for loop with axios, how do I know if all data are loaded

this.allMyFacilities = response[1].data
for (let i = 0; i < this.allMyFacilities.length; i++) {
axios
.get(facilityUsed(this.allMyFacilities[i].id))
.then((response) => {
this.facilitiesOnSupplyChain[this.allMyFacilities[i].id] = response.data
})
.catch((err) => {
// An error will also be thrown if you use cancel.
console.error(err)
})
}
I have such a code. I don't know how many facilities do I have. that is I loop it, But each facility has its own Axios. I want to how do I know all data from the loop are completely loaded
You can use Promise.all for this:
this.allMyFacilities = response[1].data
const promises = this.allMyFacilities.map((myFacility) => {
return axios
.get(facilityUsed(myFacility.id))
.then((response) => {
this.facilitiesOnSupplyChain[myFacility.id] = response.data
return response.data;
}).catch((err) => {
// An error will also be thrown if you use cancel.
console.error(err)
});
});
Promise.all(promises).then((allResponsesArray) => /* do sth */)
Assuming that all the asynchronous calls can be made independently, you can leverage upon the Promise.all functionality to achieve this.
this.allMyFacilities = response[1].data
await Promise.all(this.allMyFacilities.map(myFacility) => {
return axios.get(facilityUsed(myFacility.id))
.then(data => this.facilitiesOnSupplyChain[myFacility.id] = data);
});
This will allow you to execute all the axios calls in parallel, and hence decrease the overall execution time of the whole process.
PS:
I have written an await statement hance assuming that you will be wrapping this functionality in an async function.

IBM Cloud Function produce no output

I have some troubles while running this IBM Cloud Function:
/**
*
* main() will be run when you invoke this action
*
* #param Cloud Functions actions accept a single parameter, which must be a JSON object.
*
* #return The output of this action, which must be a JSON object.
*
*/
function main(params) {
const https = require('https');
https.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
console.log(JSON.parse(data).explanation);
});
}).on("error", (err) => {
console.log("Error: " + err.message);
});
}
My problem is that the first invokes of this function (at least the first 3-4) produce no output. The subsequent calls run properly and the log is correctly shown. How can I fix this unpredictable behaviour? I'd like, of course, to retrieve my data at first call of this function. Thanks.
Node.js uses an non-blocking asynchronous programming model. This main function returns before the HTTP response is available.
Returning a Promise will allow you to wait on the HTTP response.
function main(params) {
return new Promise((resolve, reject) => {
const https = require('https');
https.get('https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY', (resp) => {
let data = '';
// A chunk of data has been recieved.
resp.on('data', (chunk) => {
data += chunk;
});
// The whole response has been received. Print out the result.
resp.on('end', () => {
const explanation = JSON.parse(data).explanation
console.log(explanation);
resolve({ explanation })
});
}).on("error", (err) => {
console.log("Error: " + err.message);
reject({ error: err.message })
});
})
}
Two additional things to check:
Make sure to append .json to your endpoint
Example: https://<ibm-domain>/api/v1/web/<username>/default/<function>.json
Make sure to select Enable as Web Action in the Endpoints sidebar menu.
Also, you should be able to return an async main function in lieu of the Promise object.
async function main(params) {
try {
// some `await` function
} catch (e) {
// catch `await` errors
}
}
module.exports = main;

How do I resolve a promise in Node.js?

I am trying to make a simple weather application based on Node.js, like this one. My problem is that every mechanism I see is based on promises, and I don't understand the concept.
So, the code I see everywhere is like:
yrno.getWeather(LOCATION).then((weather) => {
weather.getFiveDaySummary().then((data) => console.log('five day summary', data));
weather.getForecastForTime(new Date()).then((data) => console.log('current weather', data));
})
.catch((e) => {
console.log('an error occurred!', e);
});
However, I was unable to find a way to resolve these promises and save the five day summary to a variable for later use.
How do I proceed?
Thanks,
Robin
Assign the Promise returned from yrno.getWeather(LOCATION) call to a variable.
Use Promise.all() to return results from both weather.getFiveDaySummary() and weather.getForecastForTime(new Date()) calls.
Chain .then() to the result of call to get the data at initial and subsequent .then() chained to variable identifier which returned initial Promise values.
let weatherData = yrno.getWeather(LOCATION).then(weather => {
// note `return`, alternatively omit `return` and `{`, `}` at arrow function
// .then(weather => Promise.all(/* parameters */))
return Promise.all([weather.getFiveDaySummary()
, weather.getForecastForTime(new Date())]);
});
weatherData
// `results` is array of `Promise` values returned from `.then()`
// chained to `yrno.getWeather(LOCATION).then((weather)`
.then(results => {
let [fiveDaySummary, forecastForTime] = results;
console.log('five day summary:', fiveDaySummary
, 'current weather:', forecastForTime);
// note `return` statement, here
return results
})
.catch(e => {
// `throw` `e` here if requirement is to chain rejected `Promise`
// else, error is handled here
console.log('an error occurred!', e);
});
// weatherData
// .then(results => { // do stuff with `results` from first `weatherData` call })
// .catch(e => console.log(e));
An alternative to using promises directly is to use await/async.
// weather.js
const yrno = require('yr.no-forecast')({
version: '1.9', // this is the default if not provided,
request: {
// make calls to locationforecast timeout after 15 seconds
timeout: 15000
}
});
const LOCATION = {
// This is Dublin, Ireland
lat: 53.3478,
lon: 6.2597
};
async function getWeather() {
let weather = await yrno.getWeather(LOCATION);
let fiveDaySummary = await weather.getFiveDaySummary();
let forecastForTime = await weather.getForecastForTime(new Date());
return {
fiveDaySummary: fiveDaySummary,
forecastForTime: forecastForTime,
}
}
async function main() {
let report;
try {
report = await getWeather();
} catch (e) {
console.log('an error occurred!', e);
}
// do something else...
if (report != undefined) {
console.log(report); // fiveDaySummary and forecastForTime
}
}
main(); // run it
you can run this (node.js 7) with:
node --harmony-async-await weather
You can use await/async on older targets by using Babel or Typescript to transpile it down for you.
Bonus (based off your comments) - I wouldn't do it this way, but just to show you it can be done:
const http = require('http');
const port = 8080;
http.createServer(
async function (req, res) {
let report = await getWeather(); // see above
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write("" + JSON.stringify(report.fiveDaySummary));
res.end('Hello World\n');
})
.listen(port);
again with node --harmony-async-await weather or transpile it.

Categories