#node.js #express #es6-promise
#node.js #экспресс #es6-обещание
Вопрос:
Я пытаюсь создать async GET api в ExpressJS, используя promises. однако по какой-то причине результаты не синхронизируются должным образом. API принимает URL-адрес, анализирует его параметры (каждый параметр является ссылкой на веб-сайт) из URL-адреса, отправляет запрос на каждую ссылку, получает внутренний текст <title>
тега, добавляет его к строке и возвращает в качестве ответа. Вот код
var parseLinks = (addresses) => {
let html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>result</title>
</head>
<body>
`;
let promise = new Promise((resolve, reject) => {
try {
html = html "<ul>";
addresses.forEach(address => {
request.get(`https://${address}`, (req, res, body) => {
const $ = cheerio.load(body);
const title = $('title').text();
html = html `<li>${address} - "${title}"</li>`;
});
});
html = html `
</ul>
</body>
</html>
`
resolve(html)
} catch (error) {
html = html `
<h1>an exception has occurred during parsing</h1>
</body>
</html>
`
reject(html)
}
});
return promise;
};
app.get("/I/want/title", (req, res) => {
let addresses = url.parse(`${host}${req.url}`, true);
new Promise(resolve => resolve([].concat(addresses.query.address)))
.then(addresses => parseLinks(addresses))
.then(data => {
console.log(data);
res.writeHead(200, header);
res.write(data);
res.end();
})
.catch(error => {
console.log(error);
});
}).listen(port, () =>
console.log(`server listening on http://localhost:${port}`)
);
ожидаемый результат должен быть для URL http://localhost:3000/I/want/title/?address=www.google.comamp;address=www.dawn.com/magazines
<html>
<head></head>
<body>
<h1> Following are the titles of given websites: </h1>
<ul>
<li> google.com - "Google" </li>
<li> www.dawn.com/events/ - "Events - DAWN.COM" </li>
</ul>
</body>
</html>
но мой результат
<html>
<head></head>
<body>
<h1> Following are the titles of given websites: </h1>
<ul>
</ul>
</body>
</html>
Комментарии:
1.Вам нужно
resolve
внутриget
обратного вызова запроса, а не снаружи.2. не могли бы вы объяснить немного подробнее?
3. С вашим кодом в том виде, в каком он есть сейчас,
resolve
будет вызван сразу после выполнения запроса. Вам нужно вызватьresolve
внутри обратного вызова, который вы предоставляетеrequest.get
4. можете ли вы показать какой-либо пример? вся концепция действительно разочаровывает меня.
Ответ №1:
Ваша идея с Promise
развивается в правильном направлении. Проблема в том, что ваши запросы изменяют html
переменную только после того, как вы уже resolve()
выполнили свое обещание.
Вместо этого каждый из них должен создать обещание, и вам нужно дождаться разрешения всех этих обещаний (запросов).
Что вам нужно сделать:
- возвращать обещание для каждого результата
request.get()
- используйте
map()
вместоforEach()
, которое вернет массив этих обещаний - подождите, пока все эти обещания разрешатся с помощью
Promise.all()
, чтобы создать новое обещание, которое разрешится, когда все данные обещания будут выполнены - объединить все строки результатов
Это выглядело бы так:
Promise.all(addresses.map(address => {
return new Promise(resolve => request.get(`https://${address}`, (req, res, body) => {
const $ = cheerio.load(body);
const title = $('title').text();
resolve(`<li>${address} - "${title}"</li>`);
});
}).then(results => {
html = results.join()
})
Помните, что html
переменная надежно заполняется только внутри then()
обратного вызова, даже если она определена глобально.