Async и Await
Часть раздела Основы Node.js путешествия по Node.js на Coddy. Урок 23 из 33.
Цепочка вызовов .then читается не так, как вы мыслите о шагах: прочитать, затем разобрать, затем сохранить.
Внутри функции async оператор await приостанавливает выполнение, пока промис не завершится, и возвращает его значение. При сбое промис выбрасывает исключение, поэтому его можно обработать с помощью try и catch. В модуле await также работает на верхнем уровне.
import { readFile } from 'node:fs/promises';
const loadConfig = async () => {
try {
return JSON.parse(await readFile('config.json', 'utf8'));
} catch {
return {};
}
};
const config = await loadConfig();
console.log(config.port ?? 3000);Функция async всегда возвращает промис, поэтому вызывающий её код тоже должен использовать await. Если забыть await, вы получите сам промис — Promise { <pending> }, — а не его значение.
Задание
ЛегкоНа этом шаге открывается подготовленный для курса проект инструмента командной строки для работы с заметками, а не файлы проекта из урока Promises (включая count.js). Ваша предыдущая работа остаётся в своём уроке. Перед редактированием store.js прочитайте предоставленные файлы. В этой копии есть средство чтения заметок на основе промисов и обработка ошибок.
Теперь store.js использует версии readFile и writeFile, возвращающие промисы, а его методы объявлены как async. Но выполнение пока не ожидает завершения промисов, поэтому каждая команда не работает. Добавьте await везде, где нужно дождаться завершения промиса перед следующим шагом.
- В
store.js, вload, дождитесь завершенияreadFile, прежде чемJSON.parseиспользует текст. - В
store.jsфункцияloadобъявлена какasync, поэтому она возвращает промис. Дождитесь его там, где хранилище задаёт значениеnotes. Модуль может использоватьawaitна верхнем уровне, вне любой функции. - В
store.jsдождитесь завершенияwriteFileвsave, а также дождитесь завершенияthis.save()вaddиremove. - В
notes.jsметодыstore.addиstore.removeтеперь объявлены какasync. Дождитесь их выполнения в веткахaddиdone, чтобыnиremovedсодержали значения, а не промисы.
Всего получится семь операторов await. После этого все команды будут работать как прежде. Команда list показывает заметки из notes.json. Если начать с двух заметок, команда add Buy bread выводит Saved note 3., а затем команда done 1 выводит Done: Buy milk.
Попробуйте: до внесения изменений введите list в поле после $ node notes.js. Вы увидите notes.json is damaged: ..., но с файлом всё в порядке: в JSON.parse передали промис вместо текста файла. После внесения изменений команда list снова покажет заметки. Затем введите add Buy bread и убедитесь, что появилось Saved note 3.. Введите done 1 и убедитесь, что появилось Done: Buy milk.
Попробуйте сами
import { existsSync } from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';
const file = 'notes.json';
// readFile и writeFile теперь возвращают промисы. Дождитесь выполнения каждого с помощью await.
const load = async () => {
if (!existsSync(file)) {
return [];
}
try {
return JSON.parse(readFile(file, 'utf8'));
} catch (err) {
console.error(`notes.json is damaged: ${err.message}`);
process.exit(1);
}
};
const store = {
notes: load(),
async save() {
writeFile(file, JSON.stringify(this.notes, null, 2));
},
async add(text) {
this.notes.push(text);
this.save();
return this.notes.length;
},
async remove(n) {
const [text] = this.notes.splice(n - 1, 1);
this.save();
return text;
},
};
export default store;
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Основы Node.js
Потренируйтесь самостоятельно: Песочница Express