How do I call three requests in order? - javascript

I am learning Node. I have a console app that must make requests to web services in order. Specifically, I need to make three requests in order. In an attempt to make these requests, I'm using the built-in HTTPS module. I have one request successfully executing. But, I need to make three in succession. I'm not sure how to do this. Right now, I have:
console.log('Running Request #1...');
var options = {
host: 'example.com',
path: '/api/service',
port: 443,
method: 'GET',
headers: {
'api-key': '[Hidden]'
}
};
var req = https.request(options, (res) => {
res.on('data', (d) => {});
});
req.end();
req.on('error', (e) => {
console.error(e);
});
I'm not sure how to call my three requests in order. Yet, at the same time, gracefully handling an error. If I had promises, I would know how to chain them together and just use the catch handler. But, I'm not sure how to chain together requests since the HTTPS module uses the Arrow function syntax.
Any help is appreciated it.

Try this:
var https = require('https');
var urls = ['url1', 'url2', 'url3'];
var request = function(url) {
console.log(url);
return new Promise((resolve, reject) => {
https.get(url, (res) => {
res.on('end', () => {
resolve('what');
});
res.on('data', data =>{
});
}).on('error', e => {
reject(e);
});
});
};
var promise = request(urls.shift());
while(urls.length > 0) {
let url = urls.shift();
promise = promise.then(function() {
return request(url);
});
}
promise.catch(e => console.log);

consider use promise with reduce,something like this
var urls=['u1','u2','u3'];
var er=0
function getPromise(url) {
return new Promise(function (resolve,reject) {
setTimeout(function () {
console.log(url+ " is resolved in 2 sec")
er++
if(er==1)
{
reject(url)
}else{
resolve(url)
}
},2000)
})
}
urls.reduce(function (pre,cur) {
return pre.then(function () {
return getPromise(cur)
})
},new Promise(function (resolve,reject) {
resolve(null)
}))
.then(function (result) {
console.log("final result is "+result)
},function (e) {
console.log("something wrong happens : "+e)
})
Play with the code,I think it is want you want

Related

test http.request with options using jest

Im learning using Jest and i faced this function,
but not sure how to start testing, it has inside an http.request({}, (res) => {})
and not sure how can i mock this http.request inside the current function:
also the resolve and reject from the current promise,
in advance Thank You!
const options = {
hostname: 'myHostName'
port: 'myPort',
path: 'myPath',
method: 'GET'
};
function myRequest(options) {
return new Promise((resolve, reject) => {
const req = http.request(options, (res) => {
if (res.statusCode === 200) {
let responseData = '';
res.on('data', (data) => {
responseData += data;
});
res.on('end', () => {
const response = JSON.parse(responseData);
resolve([response, true]);
});
} else {
reject(`res.message`);
}
});
req.on('error', (error) => {
reject(error);
});
req.end();
});
}

Forcing an async node.js result in azure

I usually work with other programming languages, need to implement a bit of node.js code though, which I am entirely new to: For now, I just want the result of my azure function app to be actually dependent on me calling the api. I put the HTTP call into a promise and am waiting for the result:
module.exports = async function (context, req) {
context.res = {
body: {
"data": [{
"value": "start"
}]
}
};
await callapi(context);
};
function callapi(context){
var options = {
host: 'jsonplaceholder.typicode.com',
port: 443,
path: '/todos/1',
method: 'GET'
};
var p1 = new Promise(
function(resolve, reject) {
callback = function(httpres) {
var str = '';
response.on('error', function (err) {
context.res = {body: {"data": [{"value": "error"}]}};
});
httpres.on('data', function (chunk) {
str += chunk;
});
httpres.on('end', function () {
resolve(str);
});
}
https.request(options, callback).end();
}
);
p1.then(function(reqhtml) {
context.res = {
body: {
"data": [{
"value": "it worked"
}]
}
};
})
}
My expectiation for this was that it would - depending on if the server could be reached - either return (a context with) the value "it worked" or "error", however it doesnt wait for the promise and just returns "start".
How do I wait for the async function in Azure? I do not have code outside of this; this function is only called via another API, from where I can only manipulate results in a restrained way with a graphical user interface. Is there a way to force node.js to wait inside this function?
I've cleaned up your code a bit.
module.exports = async function (context, req) {
const value = await callapi();
context.res = {
body: {
data: [{
value
}]
}
};
};
// this function does not need any parameters,
// it's sole job is to make a https request to a static endpoint
// and return a value based on wether there was an error or not
function callapi(){
var options = {
host: 'jsonplaceholder.typicode.com',
port: 443,
path: '/todos/1',
method: 'GET'
};
return new Promise(function(resolve, reject) {
const req = https.request(options, res => {
let str = "";
res.on('data', function(chunk) {
str += chunk;
});
res.on('end', function() {
resolve(str);
});
});
res.on('error', reject);
req.end();
})
.then(() => "it worked")
.catch(() => "error");
}
For now, I just want the result of my azure function app to be actually dependent on me calling the api.
Since your callapi does not return anything await callapi() will await undefined and this will resolve in the next tick. (Basically, immediately after all current syncronous code has been executed and long before the Server could have sent any data back to you)
2nd: Mutating objects is frowned upon, because it's (mentally) hard to keep track of all the places that are potentially affected by this change; therefore it's dangerous. better return a copy with the changes.
3rd: keep it simple callapi does nothing where it would need to know anything about the context
The NPM library deasync solves the problem:
module.exports = async function (context, req) {
var uri = <uri>;
var source = get_source_at(uri)
context.res = { body: { "data": [{"value": source}] } };
};
function get_source_at(uri){
var request = require("request");
var deasync = require("deasync");
var source;
request({ uri:uri, headers: <headers>}
, function (error, response, body) {
source = body;
});
while(source === undefined) { //wait until async HTTPS request has finished
deasync.runLoopOnce();
}
return source;
}

Writing a generic HTTP request in NodeJS

I am making an app that creates multiple HTTP requests to different APIs, and would like to abstract the "sending" and "getting" portion of the data into a utility function that can be exported. It should be async. So far, I have created the following, but it won't work since request.end() needs to be declared somewhere, and I'm stuck here. Thanks!
Note: the resulting data should be able to be easily returned to some other function on order to do other work with it later on.
exports.handleHttpRequests = (url) => {
return new Promise((resolve, reject) => {
const request = http.request(url, response => {
let chunks = [];
res.on('data', chunk => {
chunks.push(chunk);
});
res.on('end', () => {
let body = Buffer.concat(chunks).toString();
resolve(body);
})
})
}).then(data => {
return data;
});
}
Hey you already done all, just call request.end() inside your new Promise callback & also handle error . And do what you want to do with resolve ,reject.
exports.handleHttpRequests = (url) => {
return new Promise((resolve, reject) => {
const request = http.request(url, res => {
let chunks = [];
res.on('data', chunk => {
chunks.push(chunk);
});
res.on('end', () => {
let body = Buffer.concat(chunks).toString();
resolve(body);
})
});
//handling error
request.on('error', (e) => {
reject('problem with request: ' + e.message);
});
//here you have to put request.end()
request.end();
})
};

Is npm request asynchronous?

I can't understand Javascript asynchronous behavior.
I always thought that 'request' module would be synchronous. I used it , though, in my code, and something went absolutely wrong.
An example:
download_page = function(item) {
page = request.get( { url: 'http://test-fdfdfd.com/' + String(item) })
}
node = new App();
node.on('ready', () => {
console.log("Ready.");
Array.from(Array(3).keys()).forEach(item => download_page(item));
node.stop()
})
In that code, node should stop only after the three requests were completed. However, that didn't happen and I don't know why.
Could someone give me an explanation?
request is actually asynchronous.
You might want to return a Promise from your function, and then Promise.all all of them.
download_page = function(item) {
return new Promise((resolve, reject) => {
request.get( { url: 'http://test-fdfdfd.com/' + String(item) }, (err, data) => {
if (err) {
reject(err);
return;
}
resolve(data);
});
});
}
node = new App();
node.on('ready', () => {
console.log("Ready.");
Promise.all(Array.from(Array(3).keys()).map(item => download_page(item)));
node.stop()
})

Return data from node promises

I am currently trying to fetch data from the Spotify API with promises, and yesterday I got tremendous help for another question, regarding the same topic: "Loop the object returned from node promise and feed to the next .then".
What I do is first getting the tracks from my playlist, and then call another api which fetches the artists. Lastly I call another api which gets the artist images.
Now my question is: how do I return the data that I get from my promises?
This is my function that gets the playlist urls:
function getPlaylists(access_token) {
var options = {
url: 'https://api.spotify.com/v1/me/playlists',
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
return new Promise(function(resolve, reject) {
request.get(options, function(error, response, body) {
var playlists = body.items;
var playlistArray = [];
playlists.forEach(function(playlist) {
var name = playlist.name;
var url = playlist.tracks.href;
playlistArray.push(url);
});
if(!error) {
resolve(playlistArray);
} else {
reject(error);
}
});
});
}
This one gets the artists:
function getArtists(url,access_token) {
var params = {
url: url,
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
return new Promise(function(resolve, reject) {
request.get(params, function(error, response, body) {
var tracks = body.items;
var artistArray = [];
tracks.forEach(function(artists) {
let allArtists = artists.track.artists;
allArtists.forEach(function(artist) {
artistArray.push(artist);
});
})
if(!error) {
resolve(artistArray);
} else {
reject(error);
}
});
})
}
And this one gets the artist image:
function getArtistImages(artistId) {
var options = {
url: 'https://api.spotify.com/v1/artists/' + artistId,
json: true
};
return new Promise(function(resolve, reject) {
request.get(options, function(error, response, body) {
if(error != null) {
reject(error);
} else {
resolve(body);
}
});
})
}
EDIT EDIT
The way I call these functions is like this:
getPlaylists(access_token)
.then(playlists => Promise.all(playlists.map(playlist =>
getArtists(playlist, access_token)))
.then(artists => {
artists.map(artist => {
artist.map(a => {
console.log(a);
let component = renderToString(
<App>
<Table artists={a} />
</App>
);
res.send(
component
)
})
})
}));
It only returns the first result - obviously because it only gets to loop through the forEach loop once, before "res.send()", so how do I make sure that it loops through all artists, before I render the view? I believe I have to do another Promise.all(), but I am not sure where - does anyone have an idea?
I appreciate it :)
Its old post but I think it can be helpfull for someone.
I wrote a plugin to perform foreach based on promises supporting concurrency. I see you dont need concurrency what turn things more simple to apply other solutions.
I wrote a code by your code using my plugin. It works!
'use strict';
var request = require('request')
var promiseForeach = require('promise-foreach')
function getPlaylists(access_token) {
var options = {
url: 'https://api.spotify.com/v1/me/playlists',
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
return new Promise(function (resolve, reject) {
request.get(options, function (error, response, body) {
var playlists = body.items;
var playlistArray = [];
playlists.forEach(function (playlist) {
var name = playlist.name;
var url = playlist.tracks.href;
playlistArray.push(url);
});
if (!error) {
resolve(playlistArray);
} else {
reject(error);
}
});
});
}
function getArtists(url, access_token) {
var params = {
url: url,
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
return new Promise(function (resolve, reject) {
request.get(params, function (error, response, body) {
var tracks = body.items;
var artistArray = [];
tracks.forEach(function (artists) {
let allArtists = artists.track.artists;
allArtists.forEach(function (artist) {
artistArray.push(artist);
});
})
if (!error) {
promiseForeach.each(artistArray,
[function (artist) {
return getArtistImages(artist.id)
}],
function (arrayOfResultOfTask, currentList) {
return {
artistId: currentList.id,
artistName: currentList.name,
artistImages: arrayOfResultOfTask[0].images
}
},
function (err, newList) {
if (err) {
console.error(err)
return;
}
resolve(newList)
})
} else {
reject(error);
}
});
})
}
function getArtistImages(artistId) {
var options = {
url: 'https://api.spotify.com/v1/artists/' + artistId,
headers: { 'Authorization': 'Bearer ' + access_token },
json: true
};
return new Promise(function (resolve, reject) {
request.get(options, function (error, response, body) {
if (error != null) {
reject(error);
} else {
resolve(body);
}
});
})
}
var access_token = 'YOUR-TOKEN';
getPlaylists(access_token)
.then(playlists => {
promiseForeach.each(playlists,
[function (playlist) {
return getArtists(playlist, access_token)
}],
function (arrayOfResultOfTask, currentList) {
return {
playlistURL: currentList,
artist: arrayOfResultOfTask[0]
}
//return renderToString(
// <App>
// <Table artists={render} />
// </App>
//);
},
function (err, newList) {
if (err) {
console.error(err)
return;
}
res.send(newList)
})
});
The plugin: https://www.npmjs.com/package/promise-foreach
I hope it helps someone!
If I understand your problem correctly, it sounds like you are tripping up on how to actually use the result of Promises.
Asynchronous applications:
Promises encapsulate an asynchronous result, which is made available through the callback passed to then().
This asynchronous-ism will cascade throughout your application.
There are a number of ways applications manage the reality of asynchronous results: events, callbacks, observables, promises...
Example (using events):
This is a crude and untested example of how the data from an asynchronous request can get injected into your view. When my asynchronous request calls my then() callback, I update my model which triggers an event to re-render my view.
There are totally issues with this example (i.e. what if I don't want to rerender my whole view? what if my getArtists() returns sooner than my view can render it's loading state?). But for simplicity, we won't go there.
+function(){
var _view = $('#viewport');
var _model = {...}
var _spotifyClient = new SpotifyClient(); // this contains method similar to those you included in your question
_view.on('load', onLoad);
_view.on('model:update', onModelUpdate);
function onLoad() {
_spotifyClient
.getArtists()
.then(function(result) {
// when the getArtists() request has responded, I can update my model.
updateModel({ isLoading: false, artists: result });
})
// this will happen immediately after starting the "getArtists()" request.
udpateModel({ isLoading: true });
}
function updateModel(mod) {
for(var p in mod) {
_model[p] = mod[p];
}
_view.trigger('model:update', _model);
}
function onModelUpdate(model) {
refreshView();
}
function refreshView() {
var viewModel = buildTemplateModel(_model);
renderTemplate(_view, viewModel);
}
}();
I encourage you to research some view frameworks such as angular, knockout, or react. I also encourage you to research Reactive Extensions which provides an observable interface and many utilities regarding asynchronous streams.
Note on side effects:
Having the result of a promise trigger an event can be classified as a "side-effect". You should keep side-effects to a minimum in our application, and they really only belong in the controller / main part of your application.
If you are using promises in your application, reusable library classes and functions, that operate on promises, should return promises.
You need to call res.send on the array of all results, not on each one separately:
getPlaylists(access_token).then(playlists =>
Promise.all(playlists.map(playlist =>
getArtists(playlist, access_token)
))
).then(artists => {
res.send(artists.map(artist =>
artist.map(a => {
console.log(a);
return renderToString(
<App>
<Table artists={a} />
</App>
);
})
))
});
Also your parenthesis were slightly misnested (didn't match the indentation of the then calls) but that didn't lead to the problem.

Categories