|
1 | 1 |
|
2 | | -# Генераторы |
| 2 | +# Генераторы [todo] |
3 | 3 |
|
4 | 4 | Генераторы -- новый вид функций в современном JavaScript. Они отличаются от обычных тем, что могут приостанавливать своё выполнение, возвращать промежуточный результат и далее возобновлять его позже, в произвольный момент времени. |
5 | 5 |
|
@@ -332,4 +332,119 @@ alert( generator.next(9).done ); // true |
332 | 332 | Исключением является первый вызов `next`, который не может передать значение в генератор, т.к. ещё не было ни одного `yield`. |
333 | 333 |
|
334 | 334 |
|
| 335 | +## generator.throw |
| 336 | + |
| 337 | +Как мы видели в примерах выше, внешний код может вернуть генератору в качестве результата `yield` любое значение. |
| 338 | + |
| 339 | +Можно "вернуть" не только результат, но и ошибку! |
| 340 | + |
| 341 | +Это делает вызов `generator.throw(err)`. |
| 342 | + |
| 343 | +Например: |
| 344 | + |
| 345 | + |
| 346 | +```js |
| 347 | +//+ run |
| 348 | +'use strict'; |
| 349 | + |
| 350 | +function* gen() { |
| 351 | + // Передать вопрос во внешний код и подождать ответа |
| 352 | + try { |
| 353 | + let result = yield "Сколько будет 2 + 2?"; |
| 354 | + |
| 355 | + alert(result); |
| 356 | + } catch(e) { |
| 357 | + alert(e); |
| 358 | + } |
| 359 | +} |
| 360 | + |
| 361 | +let generator = gen(); |
| 362 | + |
| 363 | +let question = generator.next().value; |
| 364 | + |
| 365 | +*!* |
| 366 | +generator.throw(new Error("ответ не найден в моей базе данных")); |
| 367 | +*/!* |
| 368 | +``` |
| 369 | + |
| 370 | +"Вброшенная" извне ошибка обрабатывается как обычно. Она возникает в строке с `yield` и может быть перехвачена `try..catch`, как продемонстрировано выше. |
| 371 | + |
| 372 | + |
| 373 | +# Плоский асинхронный код |
| 374 | + |
| 375 | +Одна из основных областей применения генераторов -- написание "плоского" асинхронного кода. |
| 376 | + |
| 377 | +Общий принцип такой: |
| 378 | +<ul> |
| 379 | +<li>Генератор `yield'ит` не просто значения, а промисы.</li> |
| 380 | +<li>Есть специальная "функция-чернорабочий" `execute(generator)` которая запускает генератор, последовательными вызовами `next` получает из него эти промисы, ждёт их выполнения и возвращает в генератор результат, пока генератор не завершится.</li> |
| 381 | +<li>Последнее значение генератора `execute` возвращает через промис, коллбэк или просто использует, как в примере ниже.</li> |
| 382 | +</ul> |
| 383 | + |
| 384 | +Получается примерно так: |
| 385 | + |
| 386 | +```js |
| 387 | +//+ run |
| 388 | +'use strict'; |
| 389 | + |
| 390 | +function* showUserAvatar() { |
| 391 | + |
| 392 | + let userFetch = yield fetch('/article/generator/user.json'); |
| 393 | + let userInfo = yield userFetch.json(); |
| 394 | + |
| 395 | + let githubFetch = yield fetch(`https://api.github.com/users/${userInfo.name}`); |
| 396 | + let githubUserInfo = yield githubFetch.json(); |
| 397 | + |
| 398 | + let img = new Image(); |
| 399 | + img.src = githubUserInfo.avatar_url; |
| 400 | + img.className = "promise-avatar-example"; |
| 401 | + document.body.appendChild(img); |
| 402 | + |
| 403 | + yield new Promise(resolve => setTimeout(resolve, 3000)); |
| 404 | + |
| 405 | + img.remove(); |
| 406 | + |
| 407 | + return img.src; |
| 408 | +} |
| 409 | + |
| 410 | +function execute(generator, yieldValue) { |
| 411 | + |
| 412 | + let next = generator.next(yieldValue); |
| 413 | + |
| 414 | + if (!next.done) { |
| 415 | + next.value.then( |
| 416 | + result => execute(generator, result), |
| 417 | + err => generator.throw(err) |
| 418 | + ); |
| 419 | + } else { |
| 420 | + // return из генератора (его результат) |
| 421 | + alert(next.value); |
| 422 | + } |
| 423 | + |
| 424 | +} |
| 425 | + |
| 426 | +*!* |
| 427 | +execute( showUserAvatar() ); |
| 428 | +*/!* |
| 429 | +``` |
| 430 | + |
| 431 | + |
| 432 | + |
| 433 | + |
| 434 | +[head] |
| 435 | +<style> |
| 436 | +.promise-avatar-example { |
| 437 | + border-radius: 50%; |
| 438 | + position: fixed; |
| 439 | + right: 0; |
| 440 | + top: 0; |
| 441 | +} |
| 442 | +</style> |
| 443 | +[/head] |
| 444 | + |
| 445 | + |
| 446 | + |
| 447 | + |
| 448 | + |
| 449 | + |
335 | 450 |
|
0 commit comments