问题
I would like to call my "app.get('/news/news-desc', (req, res)" method after "app.get('/news/api/:newsName', function(req, res)" is completed.
Here is my code:
let articleUrlArray = [];
app.get('/news/api/:newsName', function(req, res) {
const API_KEY = 'example';
let data = '';
const techCrunchURL = `https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=${API_KEY}`
switch(req.params.newsName) {
case 'tech-crunch':
request(techCrunchURL, function(err, response, html) {
let formattedData = JSON.parse(response.body);
for(let i = 0; i < formattedData.articles.length; i++) {
articleUrlArray.push(formattedData.articles[i].url);
}
data = response.body;
res.setHeader('Content-Type', 'application/json');
res.send(data);
});
break;
default:
data = 'Please type in correct news source';
break;
}
})
const checkBody = res => (err, response, html) => {
const $ = cheerio.load(html);
const articleContent = $('.article-content').children('p')
const bodyOne = articleContent.eq(0).text()
const bodyTwo = articleContent.eq(1).text()
const isExtensive = bodyOne.split(' ').length > 50
res(isExtensive ? { bodyOne } : { bodyOne, bodyTwo })
}
const getArticle = article => new Promise(res => request(article, checkBody(res)))
app.get('/news/news-desc', (req, res) => {
Promise.all(articleUrlArray.map(getArticle)).then(data => res.send(JSON.stringify(data)))
})
As you can see, the first method calls the "newsapi.org" and gets 10 articles. Then it would only extract the urls of those articles and push them into articleUrlArray.
After the urls have been pushed into the articleUrlArray, it would look like this:
let articleUrlArray = [ 'https://techcrunch.com/2018/05/19/shared-housing-startups-are-taking-off/',
'https://techcrunch.com/2018/05/19/shared-housing-startups-are-taking-off/',
'https://techcrunch.com/2018/05/19/my-data-request-lists-guides-to-get-data-about-you/',
'https://techcrunch.com/2018/05/19/siempos-new-app-will-break-your-smartphone-addiction/',
'https://techcrunch.com/2018/05/19/la-belle-vie-wants-to-compete-with-amazon-prime-now-in-paris/',
'https://techcrunch.com/2018/05/19/apple-started-paying-15-billion-european-tax-fine/',
'https://techcrunch.com/2018/05/19/original-content-dear-white-people/',
'https://techcrunch.com/2018/05/19/meet-the-judges-for-the-tc-startup-battlefield-europe-at-vivatech/',
'https://techcrunch.com/2018/05/18/nasas-newest-planet-hunting-satellite-takes-a-stellar-first-test-image/',
'https://techcrunch.com/video-article/turning-your-toys-into-robots-with-circuit-cubes/',
'https://techcrunch.com/2018/05/18/does-googles-duplex-violate-two-party-consent-laws/' ];
It would just be filled up with urls.
Then the second method, would use the filled up articleUrlArray to do its own thing.
However, currently for my code, the second method runs first before the articleUrlArray has been filled up.
I would like to run the second method after the first method completes and the articleUrlArray has been filled up with urls.
Could you please help me with this?
回答1:
let articleUrlArray = [];
const addArticleUrl = url => articleUrlArray.push(url)
const checkBody = res => (err, response, html) => {
const $ = cheerio.load(html);
const articleContent = $('.article-content').children('p')
const bodyOne = articleContent.eq(0).text()
const bodyTwo = articleContent.eq(1).text()
const isExtensive = bodyOne.split(' ').length > 50
res(isExtensive ? { bodyOne } : { bodyOne, bodyTwo })
}
const getArticle = article => new Promise(res => request(article, checkBody(res)))
const newsDescMiddleware = app.get('/news/news-desc', (req, res) => {
Promise.all(articleUrlArray.map(getArticle)).then(data => res.send(JSON.stringify(data)))
})
const techCrunch = res => url => request(url, (err, response, html) => {
let formattedData = JSON.parse(response.body);
formattedData.articles.forEach(article => addArticleUrl(article.url))
res(response.body)
})
const getNewsByName = (newsName, url) => new Promise((res, reject) => ({
'tech-crunch': techCrunch(res)(url)
}[newsName])) || reject()
const getNewsByNameMiddleware = (req, res) => {
const API_KEY = 'example';
const techCrunchURL = `https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=${API_KEY}`
getNewsByName(req.params.newsName, url)
.then(body => {
res.setHeader('Content-Type', 'application/json');
res.send(body)
})
.catch(() => res.send('Please type in correct news source'))
}
app.get('/news/api/:newsName', getNewsByNameMiddleware, newsDescMiddleware)
Here, I made you some middlewares.
I am assuming that you don't need the response of the previous middleware.
I like to split the code by its responsibilities and write it functionally.
回答2:
You can separate the core logic of the first route to a function and re-use it in both places, if you please. however you still need to provide newsName parameter to GET '/news/news-desc' endpoint.
Example for your code.
let articleUrlArray = [];
function getNewsNames(newsName, callback) {
const API_KEY = 'example';
let data = '';
const techCrunchURL = `https://newsapi.org/v2/top-headlines?sources=techcrunch&apiKey=${API_KEY}`
switch (newsName) {
case 'tech-crunch':
request(techCrunchURL, function (err, response, html) {
let formattedData = JSON.parse(response.body);
for (let i = 0; i < formattedData.articles.length; i++) {
articleUrlArray.push(formattedData.articles[i].url);
}
data = response.body;
callback(null, data);
});
break;
default:
data = 'Please type in correct news source';
callback('Error', data);
break;
}
}
app.get('/news/api/:newsName', function (req, res) {
getNewsNames(req,params.newsName, (err, data) => {
if (!err) {
res.setHeader('Content-Type', 'application/json');
}
return res.send(data);
})
})
const checkBody = res => (err, response, html) => {
const $ = cheerio.load(html);
const articleContent = $('.article-content').children('p')
const bodyOne = articleContent.eq(0).text()
const bodyTwo = articleContent.eq(1).text()
const isExtensive = bodyOne.split(' ').length > 50
res(isExtensive ? { bodyOne } : { bodyOne, bodyTwo })
}
const getArticle = article => new Promise(res => request(article, checkBody(res)))
app.get('/news/news-desc/:newsName', (req, res) => {
getNewsNames(req.params.newsName, (err, data) => {
// by now, the articleUrlArray array will be filled
Promise.all(articleUrlArray.map(getArticle)).then(data => res.send(JSON.stringify(data)))
})
})
来源:https://stackoverflow.com/questions/50441359/node-js-call-a-method-after-another-method-is-completed