import { get } from "request";
import { writeFile } from "fs/promises";
import { promisify } from "util";
const getAsync = promisify(get);
async function main() {
let body;
try {
({ body } = await getAsync("https://en.wikipedia.org/wiki/Async/await"));
} catch(requestErr) {
console.error(requestErr);
return;
}
try {
await writeFile("async.js", body);
console.log("File written");
} catch (writeErr) {
console.error(writeErr);
}
}
main();
Generi c руководство по преобразованию любого потока обратного вызова в обещания и async
/ await
Проверить, есть ли в библиотеке API обещаний
Если да, используйте его. Многие библиотеки имеют двойной интерфейс, где вы можете вызывать их с помощью обратного вызова или вы можете вызывать их без, и они возвращают вам обещание. Или у них может быть альтернативный импорт для получения версии обещания.
Если нет, вам нужно преобразовать обратные вызовы в обещания . Имейте в виду, что это можно сделать с помощью существующей библиотеки.
Последняя альтернатива - использовать другую библиотеку, но это немного экстремально. Вам нужно будет проверить, работает ли он так же, как вы используете текущий. Тем не менее, иногда это хороший вариант, если есть более новая и лучше обслуживаемая библиотека.
Преобразуйте обратные вызовы в async
/ await
В общем преобразование выглядит так:
From
callbackAPI(data, function(error, result) {
if (error) {
//handle error
} else {
//process result
}
});
To
//assume the promisifiedAPI is the promise returning alternative of callbackAPI
try {
const result = await promisifiedAPI(data);
//process result
} catch (error) {
//handle error
}
Это может потребовать некоторых изменений в зависимости от точного использования, но обычно обратный вызов имеет error
и result
как два параметра. Это преобразуется в конструкцию try..catch
, где теперь обещанный вызов - await
ed и возвращает, а затем обрабатывает result
, при отсутствии проблемы обрабатывается в try
. Обработка ошибок происходит в блоке catch
.
Также, если еще нет верхнего уровня await
, вам нужно обернуть весь асинхронный c код в async function() {}
или async () => {}
в противном случае вы не можете использовать await
.
Шаги, предпринятые здесь
Проверить, есть ли в библиотеке API обещаний
request
Библиотека не имеет встроенного API обещаний. В документации предлагается использовать библиотеки, конвертирующие вызовы в обещания . Легкий способ - использовать utils.promisify()
для достижения этой цели, поскольку он уже находится в Node.JS.
Проверяя это, я также наткнулся на How do you right обещать запрос? (ответ Little Roys) именно об этой библиотеке. Код есть (изменен, чтобы сосредоточиться только на get
):
import { get, post } from "request";
import { promisify } from "util";
const [getAsync, postAsync] = [get, post].map(promisify);
или, если мы просто хотим сосредоточиться на get()
import { get } from "request";
import { promisify } from "util";
const getAsync = promisify(get);
Также стоит отметить, что Пакет request
устарел. В этом случае это жизнеспособная альтернатива переходу на другую библиотеку. Не нужно, но есть возможность. Я буду использовать его на протяжении всего примера, чтобы продемонстрировать, как происходит нормальное преобразование.
fs
Это действительно имеет API-интерфейс обещаний , и вы можете просто импортировать вещи из "fs/promises"
. Вот обещанная версия writeFile
.
В этом случае достаточно заменить
import { writeFile } from "fs";
на
import { writeFile } from "fs/promises";
Преобразовать обратные вызовы на async
/ await
require
From
get(
"https://en.wikipedia.org/wiki/Async/await",
(requestErr, response, body) => {
if (requestErr) {
console.error(requestErr);
} else {
//omitted for brevitiy
}
}
To
let body;
try {
({ body } = await getAsync("https://en.wikipedia.org/wiki/Async/await"));
} catch(requestErr) {
console.error(requestErr);
return;
}
Это вызовет теперь обещанный getAsync
и извлечет body
из ответа .
Если есть ошибка, мы обрабатываем ее в блоке catch
. return;
предназначен для немедленного завершения функции в случае ошибки. Я вернусь к этому позже.
Теперь, когда у нас есть body
ответа, мы можем продолжить
fs
From
writeFile("async.js", body, writeErr => {
if (writeErr) {
console.error(writeErr);
} else {
console.log("File written");
}
});
To
try {
await writeFile("async.js", body);
console.log("File written");
} catch (writeErr) {
console.error(writeErr);
}
Мы вызываем writeFile
, используя await
, что гарантирует возобновление выполнения только после завершения операции. Мы не получаем от него ответа, мы просто отслеживаем ошибки с помощью try..catch
и обрабатываем их, если они возникают. Если writeFile
успешно, мы просто go передаем console.log("File written");
async function main()
async function main() {
//execute promisified operations
}
main();
Весь блок превращается в функцию asyn c, поэтому await
может быть использован. Затем мы вызываем эту функцию. Так это немного читабельнее. Как уже упоминалось, может иметь поддержку верхнего уровня await
. Это может быть либо в будущем, либо через транспиляцию. Или, может быть, ваш код уже находится в асинхронной функции. В таких случаях вам не понадобится другая асинхронная функция, но я хотел бы продемонстрировать это на всякий случай.
try {} catch(error) { return; }
Наконец, я хочу обратиться к этой конструкции. Не люблю вложение блоков. Избежать этого можно, если переписать код таким образом (фрагмент кода свернут для краткости):
import { get } from "request";
import { writeFile } from "fs/promises";
import { promisify } from "util";
const getAsync = promisify(get);
async function main() {
try {
const { body } = await getAsync("https://en.wikipedia.org/wiki/Async/await");
try {
await writeFile("async.js", body);
console.log("File written");
} catch (writeErr) {
console.error(writeErr);
}
} catch(requestErr) {
console.error(requestErr);
}
}
main();
Однако это начинает становиться проблемой, если вам нужно несколько вызовов asyn c, которые будут в дальнейшем вкладывать блоки. Я предпочитаю ранний выход через return
, который удерживает блоки на одном уровне. Альтернативой является использование оператора throw
, если вы предпочитаете (и можете) обрабатывать сбой дальше по цепочке вызовов. Оба достигают одного и того же с точки зрения вложенности.