проблема с синхронизацией обещаний express js API

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