асинхронный / ожидающий для Node.js https.получить

#node.js #async-await

#node.js #асинхронный-ожидание

Вопрос:

Я пытаюсь упростить код с помощью async / await

Но возникли проблемы https.get с асинхронной / ожидающей структурой.

Я знаю, как это сделать с помощью сторонних модулей, но предпочитаю собственный node.js https модуль.

Приведенный ниже код не работает для меня:

 async function get_page() {

    const https = require('https')
    const url = 'https://example.com'

    const util = require('util')
    const https_get = util.promisify(https.get)

    const data = await https_get(url)

    do_awesome_things_with_data(data)
}
 

Этот код работает нормально:

 function get_page() {

    const https = require('https')
    const url = 'https://example.com'

    let data = ''

    https.get(url, res => {

        res.on('data', chunk => { data  = chunk }) 

        res.on('end', () => {

           do_awesome_things_with_data(data)

        })
    }) 
}
 

Комментарии:

1. twilio.com/blog/…

2. Я думаю, вы звоните do_awesome_things_with_data(data) в тот момент, когда данные не разрешены. Просто для отладки не могли бы вы установить время ожидания в 1 секунду между вызовом функции и обещанием

Ответ №1:

https.get не возвращает что-то, что может быть обещано, поскольку подпись обратного вызова не совпадает (err, value) , поэтому вы не можете ее дождаться.

Однако вы можете обернуть https.get вызов в обещание, например, так, а затем ожидать при вызове get_page

 const https = require('https')

async function get_page() {
    const url = 'https://example.com'

    return new Promise((resolve) => {
        https.get(url, res => {

            res.on('data', chunk => { data  = chunk }) 

            res.on('end', () => {

               resolve(do_awesome_things_with_data(data));

            })
        }) 
    })
}

// usage

(async () => await get_page())()
 

Правки

Я обновил свой ответ, включив в него примечание о https.get невозможности быть обещанным и переместил require('https') вызов функции за пределы.

Комментарии:

1. Проблема в том, что get не возвращает что-то, что promisify можно обернуть. Оператору было известно, что обещание не было возвращено.

2. require() не должен использоваться в async функции, поскольку он синхронизируется. Также плохой практикой require() является использование внутри функции, которая вызывалась бы повторно, как правило, то же самое касается util.promisify() . Они оба должны быть перемещены за пределы функции.

3. Да, оба комментария здесь верны. Я обновлю свой ответ, чтобы отразить.

4. Это элегантный минималистичный ответ, но я не понимаю, почему функция get_page должна иметь асинхронный префикс. Когда я реализую этот шаблон без этого префикса, он работает так же. Насколько я понимаю, async разрешает префиксы ожидания, но у вас их нет, и обещает возвращаемые значения, не являющиеся обещаниями, но вы их не возвращаете.

5. Это правильно @JonathanPool — по умолчанию в этом случае вам не нужен async префикс, потому что мы не await редактируем. Для согласованности и для того, чтобы следующий инженер знал, кто будет использовать get_page функцию, они сразу узнают, что эта функция возвращает обещание, и потребуется либо использовать .then() , либо await .

Ответ №2:

Вместо promisify создайте свою собственную функцию или используйте стороннюю библиотеку. Promisify не может обернуть то, что https.get возвращается.

 // generic promise method for https
const requestPromise = ((urlOptions, data) => {
  return new Promise((resolve, reject) => {
    const req = https.request(urlOptions,
      (res) => {
        let body = '';
        res.on('data', (chunk) => (body  = chunk.toString()));
        res.on('error', reject);
        res.on('end', () => {
          if (res.statusCode >= 200 amp;amp; res.statusCode <= 299) {
            resolve({statusCode: res.statusCode, headers: res.headers, body: body});
          } else {
            reject('Request failed. status: '   res.statusCode   ', body: '   body);
          }
        });
      });
    req.on('error', reject);
    req.write(data, 'binary');
    req.end();
  });
});
 

Затем назовите это так:

 async function get_page() {
    const url = 'https://example.com'
    const data = await requestPromise({url, method:'GET'})
    do_awesome_things_with_data(data)
}
 

Или просто используйте библиотеку, например, axios чтобы возвращать собственное обещание, а также обрабатывать дополнительные шаблонные случаи.

Ответ №3:

Вот как я это сделал:

 async myFunc = function {
    let url = 'http://your.data/file';
    let promise = new Promise((resolve, reject) => {
        var data = '';
        https.get(url, res => {
            res.on('data', chunk => { data  = chunk }) 
            res.on('end', () => {
               resolve(data);
            })
        }) 
    });

    let result = await promise; // wait until the promise resolves
    doStuffWithResult(result);
}; 

В моем случае я извлекал файл .json, поэтому я фактически resolve(JSON.parse(data)) возвращал чистый объект JSON.