проблема синхронизации обещаний API Express JS - PullRequest
0 голосов
/ 16 марта 2019

Я пытаюсь создать асинхронный GET API в ExpressJS, используя обещания. однако по какой-то причине результаты не синхронизируются должным образом. 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.com&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 Ответ

0 голосов
/ 17 марта 2019

Ваша идея с Promise идет в правильном направлении.Проблема в том, что ваши запросы изменяют переменную html только после того, как у вас уже есть resolve() d ваше обещание.

Каждый из них вместо этого должен создать обещание, и вам нужно дождаться всех этих обещаний (запросов) решить.

Что нужно сделать:

  • вернуть обещание для каждого результата 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(), даже если он определен глобально.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...