generator.throw
Как мы видели в примерах выше, внешний код может вернуть генератору в качестве результата yield любое значение.
…Но «вернуть» можно не только результат, но и ошибку!
Для того, чтобы передать в yield ошибку, используется вызов generator.throw(err) . При этом на строке с yield возникает исключение. Например, в коде ниже обращение к внешнему коду yield "Сколько будет 2 + 2" завершится с ошибкой:
function* gen() { try {
// в этой строке возникнет ошибка
let result = yield "Сколько будет 2 + 2?"; // (**)
alert("выше будет исключение ^^^");
} catch(e) {
alert(e); // выведет ошибку
}
}
let generator = gen();
let question = generator.next().value;
generator.throw(new Error("ответ не найден в моей базе данных")); // (*)
«Вброшенная» в строке (*) ошибка возникает в строке с yield (**) . Далее она обрабатывается как обычно. В примере выше она перехватывается
try..catch и выводится.
Если ошибку не перехватить, то она «выпадет» из генератора. По стеку ближайший вызов, который инициировал выполнение – это строка с .throw . Можно перехватить её там, как и продемонстрировано в примере ниже:
function* gen() {
// В этой строке возникнет ошибка
let result = yield "Сколько будет 2 + 2?";
}
let generator = gen();
let question = generator.next().value; try {
generator.throw(new Error("ответ не найден в моей базе данных"));
} catch(e) {
alert(e); // выведет ошибку
}
Если же ошибка и там не перехвачена, то дальше – как обычно, либо try..catch снаружи, либо она «повалит» скрипт.
Плоский асинхронный код
Одна из основных областей применения генераторов – написание «плоского» асинхронного кода. Общий принцип такой:
Генератор yield'ит не просто значения, а промисы.
Есть специальная «функция‑чернорабочий» execute(generator) которая запускает генератор, последовательными вызовами next получает из него промисы – один за другим, и, когда очередной промис выполнится, возвращает его результат в генератор следующим next .
Последнее значение генератора ( done:true ) execute уже обрабатывает как окончательный результат – например, возвращает через промис куда‑ то ещё, во внешний код или просто использует, как в примере ниже.
Напишем такой код для получения аватара пользователя с github и его вывода, аналогичный рассмотренному в статье про промисы. Для AJAX‑запросов будем использовать метод fetch, он как раз возвращает промисы.
// генератор для получения и показа аватара
// он yield'ит промисы function* showUserAvatar() {
let userFetch = yield fetch('/article/generator/user.json'); let userInfo = yield userFetch.json();
let githubFetch = yield fetch(`https://api.github.com/users/${userInfo.name}`); let githubUserInfo = yield githubFetch.json();
let img = new Image();
img.src = githubUserInfo.avatar_url; img.className = "promise‐avatar‐example"; document.body.appendChild(img);
yield new Promise(resolve => setTimeout(resolve, 3000)); img.remove();
return img.src;
}
// вспомогательная функция‐чернорабочий
// для выполнения промисов из generator function execute(generator, yieldValue) {
let next = generator.next(yieldValue); if (!next.done) {
next.value.then(
result => execute(generator, result), err => generator.throw(err)
);
} else {
// обработаем результат return из генератора
// обычно здесь вызов callback или что‐то в этом духе alert(next.value);
}
}
execute( showUserAvatar() );
Функция execute в примере выше – универсальная, она может работать с любым генератором, который yield'ит промисы.
Вместе с тем, это – всего лишь набросок, чтобы было понятно, как такая функция в принципе работает. Есть уже готовые реализации, обладающие большим количеством возможностей.
Одна из самых известных – это библиотека co , которую мы рассмотрим далее.
Do'stlaringiz bilan baham: |