#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)
})
})
}
Комментарии:
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.