Библиотека co технически позволяет писать код так:
let user = yield fetchUser(url);
// вместо
// let user = yield* fetchUser(url);
То есть, можно сделать yield генератора, co() его выполнит и передаст значение обратно. Как мы видели выше, библиотека co – довольно всеядна. Однако, рекомендуется использовать для вызова функций‑генераторов именно yield* .
Причин для этого несколько:
Делегирование генераторов yield* – это встроенный механизм JavaScript. Вместо возвращения значения обратно в co , выполнения кода библиотеки… Мы просто используем возможности языка. Это правильнее.
Поскольку не происходит лишних вызовов, это быстрее по производительности.
И, наконец, пожалуй, самое приятное – делегирование генераторов сохраняет стек.
Проиллюстрируем последнее на примере:
co(function*() {
// при запуске в стеке не будет видно этой строки yield g(); // (*)
}).catch(function(err) { alert(err.stack);
});
function* g() {
throw new Error("my error");
}
При запуске этого кода стек может выглядеть примерно так:
at g (eval at runJS …, :13:9)
at GeneratorFunctionPrototype.next (native) at onFulfilled (…/co/…/index.min.js:1:1136) at …/co/…/index.min.js:1:1076
at co (…/co/…/index.min.js:1:1039)
at toPromise (…/co/…/index.min.js:1:1740) at next (…/co/…/index.min.js:1:1351)
at onFulfilled (…/co/…/index.min.js:1:1172) at …/co/…/index.min.js:1:1076
at co (…/co/…/index.min.js:1:1039)
Детали здесь не имеют значения, самое важное – почти весь стек находится внутри библиотеки co . Из оригинального скрипта там только одна строка (первая):
at g (eval at runJS …, :13:9)
То есть, стек говорит, что ошибка возникла в строке 13 :
// строка 13 из кода выше throw new Error("my error");
Что ж, спасибо. Но как мы оказались на этой строке? Об этом в стеке нет ни слова! Заменим в строке (*) вызов yield на yield* :
co(function*() {
// заменили yield на yield* yield* g(); // (*)
}).catch(function(err) { alert(err.stack);
});
function* g() {
throw new Error("my error");
}
Пример стека теперь:
at g (eval at runJS …, :13:9)
at GeneratorFunctionPrototype.next (native) at eval (eval at runJS …, :6:10) at GeneratorFunctionPrototype.next (native) at onFulfilled (…/co/…/index.min.js:1:1136) at …/co/…/index.min.js:1:1076
at co (…/co/…/index.min.js:1:1039)
at eval (eval at runJS …, :3:1) at eval (native)
at runJS (…)
Если очистить от вспомогательных вызовов, то эти строки – как раз то, что нам надо:
at g (eval at runJS …, :13:9)
at eval (eval at runJS …, :6:10) at eval (eval at runJS …, :3:1)
Теперь видно, что (читаем снизу) исходный вызов был в строке 3 , далее – вложенный в строке 6 , и затем произошла ошибка в строке 13 .
Почему вариант с простым yield не работает – достаточно очевидно, если внимательно посмотреть на код и воспроизвести в уме, как он функционирует. Оставляем это упражнение вдумчивому читателю.
Итого, рекомендация уже достаточно обоснована – при запуске вложенных генераторов используем yield* .
Итого
Генераторы создаются при помощи функций‑генераторов function*(…) {…} .
Внутри генераторов и только внутри них разрешён оператор yield . Это иногда создаёт неудобства, поскольку в коллбэках .map/.forEach сделать
yield нельзя. Впрочем, можно сделать yield массива (при использовании co ).
Внешний код и генератор обмениваются промежуточными результатами посредством вызовов next/yield .
Генераторы позволяют писать плоский асинхронный код, при помощи библиотеки co .
Что касается кросс‑браузерной поддержки – она стремительно приближается. Пока же можно использовать генераторы вместе с Babel .
Модули
Концепция модулей как способа организации JavaScript‑кода существовала давно.
Когда приложение сложное и кода много – мы пытаемся разбить его на файлы. В каждом файле описываем какую‑то часть, а в дальнейшем – собираем эти части воедино.
Модули в стандарте ECMAScript предоставляют удобные средства для этого. Такие средства предлагались сообществом и ранее, например:
AMD – одна из самых древних систем организации модулей, требует лишь наличия клиентской библиотеки, к примеру, require.js , но поддерживается и серверными средствами.
CommonJS – система модулей, встроенная в сервер Node.JS. Требует поддержки на клиентской и серверной стороне.
UMD – система модулей, которая предложена в качестве универсальной. UMD‑модули будут работать и в системе AMD и в CommonJS.
Все перечисленные выше системы требуют различных библиотек или систем сборки для использования.
Новый стандарт отличается от них прежде всего тем, что это – стандарт. А значит, со временем, будет поддерживаться браузерами без дополнительных утилит.
Однако, сейчас браузерной поддержки почти нет. Поэтому ES‑модули используются в сочетании с системами сборки, такими как webpack , brunch
и другими, при подключённом Babel.JS . Мы рассмотрим это далее.
Do'stlaringiz bilan baham: |