diff --git a/async & performance/ch3.md b/async & performance/ch3.md index 3ca6801..73837c2 100644 --- a/async & performance/ch3.md +++ b/async & performance/ch3.md @@ -43,9 +43,9 @@ Другими словами, как только мое *будущее значение* было готово, я обменял промис значения на само значение. -Но есть и еще один возможный исход. Они называют мой номер заказа, но когда я подхожу, чтобы забрать свой чизбургер кассир с сожалению сообщает мне: "Мне жаль, но мы судя по всему остались без чизбургеров." Оставив в стороне разочарование покупателя в таком сценарии на секундочку, мы можем заметить важную характеристику *будущих значений*: они могут сигнализировать либо о завершении, либо об отказе. +Но есть и еще один возможный исход. Он называют мой номер заказа, но когда я подхожу, чтобы забрать свой чизбургер кассир с сожалению сообщает мне: "Мне жаль, но мы судя по всему остались без чизбургеров." Оставив в стороне разочарование покупателя в таком сценарии на секундочку, мы можем заметить важную характеристику *будущих значений*: они могут сигнализировать либо о завершении, либо об отказе. -Каждый раз, когда я заказываю чизбургер, я знаю что либо я рано или поздно получу чизбургер, либо получу печальные новости о нехватке чизбургеров и я должен буду придумать что-то другое на обед. +Каждый раз, когда я заказываю чизбургер, я знаю что либоя рано или поздно получу чизбургер, либо получу печальные новости о нехватке чизбургеров и я должен буду придумать что-то другое на обед. **Примечание:** В реальном коде процесс не такой простой, потому что метафорически номер заказа может никогда не быть назван, и в таком случае мы остаемся в неразрешим состоянии вечно. Мы еще вернемся к этому случаю позже. @@ -558,7 +558,7 @@ Promise.race( [ Поскольку промис можно разрешить лишь раз, любые зарегистрированные колбеки `then(..)` будут вызваны только по разу (каждый). -Конечно, если зарегистрируете один и ото же колбек более одного раза, (т.е., `p.then(f); p.then(f);`), он будет вызван столько раз, сколько был зарегистрирован. Гарантия того, что функция ответа будет вызвана только раз не препятствует вам выстрелить себе в ногу. +Конечно, если зарегистрируете один и тот же колбек более одного раза, (т.е., `p.then(f); p.then(f);`), он будет вызван столько раз, сколько был зарегистрирован. Гарантия того, что функция ответа будет вызвана только раз не препятствует вам выстрелить себе в ногу. ### Сбой при передаче параметров/окружения @@ -566,7 +566,7 @@ Promise.race( [ Если вы не разрешаете промис явно с конкретным значением, значение будет `undefined`, как и обычно в таких случаях в JS. Но если ли значение или нет, оно всегда будет передавно во все зарегистрированные (и корректные: завершение или отказ) колбеки, либо *сейчас*, или в будущем. -Что-то, о чем нужно знать: если вы вызываете `resolve(..)` или `reject(..)` с несколькими параметрами, все параметры, которые следуют за первым, будут молча проигнорированы. Хотя это и может выглядеть как нарушение гарантии, которую мы только что описали, это не совсем так потому что it это представляет собой недопустимое использование механизма промисов. Другие недопустимые случаи использования API (такие как вызов `resolve(..)` несколько раз) *защищены* похожим образом, таким образом поведение промисов тут будет консистентным (если не немного расстраивающим). +Что-то, о чем нужно знать: если вы вызываете `resolve(..)` или `reject(..)` с несколькими параметрами, все параметры, которые следуют за первым, будут молча проигнорированы. Хотя это и может выглядеть как нарушение гарантии, которую мы только что описали, это не совсем так потому что это представляет собой недопустимое использование механизма промисов. Другие недопустимые случаи использования API (такие как вызов `resolve(..)` несколько раз) *защищены* похожим образом, таким образом поведение промисов тут будет консистентным (если не немного расстраивающим). Если вы хотите передать несколько значений, вы должны обернуть их в еще одно одиночное значение, которое вы и передадите, такое как `array` (массив) или `object` (объект). @@ -599,7 +599,7 @@ p.then( JS-исключение, которое возникает от `foo.bar()`, становится отказом промиса, который вы можете захватить и обработать. -Это - важная деталь, потому что она успешно решает еще один потенциальный Залго-момент, который заключается в том, что эти ошибки повлечь синхронную реакцию тогда как не-ошибки будут асинхронными. Даже JS-исключения промимсы превращают в асинхронное поведение, тем самым значительно уменьшая шансы появления состояния гонки. +Это - важная деталь, потому что она успешно решает еще один потенциальный Залго-момент, который заключается в том, что эти ошибки повлекут синхронную реакцию тогда как не-ошибки будут асинхронными. Даже JS-исключения промимсы превращают в асинхронное поведение, тем самым значительно уменьшая шансы появления состояния гонки. Но что произойдет если промис завершен, а произошло JS-исключение во время обработки результата (в зарегистрированном колбеке `then(..)`)? Даже такие исключения не будут потеряны, но вы можете немного удивиться тому, как они обрабатываются, пока не вникните немного глубже в это: @@ -757,7 +757,7 @@ Promise.resolve( foo( 42 ) ) * Каждый раз как вы вызываете `then(..)` у промиса, он создает и возвращает новый промис, с которым мы можем составить *цепочку*. * Какое бы значение вы ни вернули из колбека завершения `then(..)` (первый параметр) - оно автоматически устанавливается как set как результата нормального завершения промиса *в цепочке* (из первого пункта). -Давайте сперва проиллюстрируем что это значит, а *затем* (then, туту игра слов) вы выведем как это помодет нам создавать асинхронные последовательности управления потоком. Рассмотрим следующее: +Давайте сперва проиллюстрируем что это значит, а *затем* (then, тут игра слов) вы выведем как это помодет нам создавать асинхронные последовательности управления потоком. Рассмотрим следующее: ```js var p = Promise.resolve( 21 ); @@ -1025,7 +1025,7 @@ var p = new Promise( function(X,Y){ Со вторым параметром легко определиться. Почти вся литература использует `reject(..)` (отвергнуть) как его имя и поскольку это в точности (и только это!) то, что он делает, это и есть очень хороший выбор для этого имени. Я бы настоятельно рекомендовал вам всегда использовать `reject(..)`. -Но вокруг первого параметра чуть больше неясностей, который в литературе о промисах часто обозначается `resolve(..)` (разрешить). Это слово очевидно связано с "resolution" (разрешение), которое и используется во всей литературе (включая эту книгу), чтобы описать установку конечного значения/состояния в промисе. Мы уже использовать "разрешить промис" несколько раз, чтобы обозначить либо завершение, или отвергнутый промис. +Но вокруг первого параметра чуть больше неясностей, который в литературе о промисах часто обозначается `resolve(..)` (разрешить). Это слово очевидно связано с "resolution" (разрешение), которое и используется во всей литературе (включая эту книгу), чтобы описать установку конечного значения/состояния в промисе. Мы уже использовали "разрешить промис" несколько раз, чтобы обозначить либо завершение, или отвергнутый промис. Но если этот параметр, по-видимому, используется для конкретно завершения промиса, почему бы не назвать его `fulfill(..)` (завершить) вместо `resolve(..)` (разрешить), чтобы быть более точным? Чтобы ответить на этот вопрос, давайте также взглянет на два метода `Promise` API: @@ -1116,7 +1116,7 @@ catch (err) { } ``` -Было бы неплохо иметь в арсенале `try..catch`, но он не работает для асинхронных операций. То есть, если только нет какой-то дополнительной поддержки среды, к к торой мы вернемся вместе с генераторами в главе 4. +Было бы неплохо иметь в арсенале `try..catch`, но он не работает для асинхронных операций. То есть, если только нет какой-то дополнительной поддержки среды, к которой мы вернемся вместе с генераторами в главе 4. В колбеках, появились некоторые стандарты для шаблонной обработки ошибок, особенно стиль "колбек ошибки идет первым": @@ -1187,11 +1187,11 @@ p.then( Это должно дать четкое представление о том, почему обработка ошибок в промисах подвержена ошибкам (каламбур). Слишком легко допустить, чтобы ошибки были "проглочены", так как это очень редко то, что вы задумывали изначально. -**Предупреждение:** Если вы используете API промисов неправильным путем и происходит ошибка, то это препятсвует правильному созданию промиса, в результате будет полученное сращу же исключение, но **не отвергнутый промис**. Некоторые примеры некорректного использования, который ломают создание промиса: `new Promise(null)`, `Promise.all()`, `Promise.race(42)`, и т.д.. Вы не сможете получить отвергнутый промис, если вы не используете API промисов достаточно корректно, чтобы на само деле создать в первую очередь сам промис! +**Предупреждение:** Если вы используете API промисов неправильным путем и происходит ошибка, то это препятсвует правильному созданию промиса, в результате будет полученное сразу же исключение, но **не отвергнутый промис**. Некоторые примеры некорректного использования, который ломают создание промиса: `new Promise(null)`, `Promise.all()`, `Promise.race(42)`, и т.д.. Вы не сможете получить отвергнутый промис, если вы не используете API промисов достаточно корректно, чтобы на само деле создать в первую очередь сам промис! ### Яма отчаяния -Джефф Этвуд заметил несколько лет назад: языки программирования часто настроены настоены по умолчанию таким образом, что разработчки попадают в "яму отчаяния" (http://blog.codinghorror.com/falling-into-the-pit-of-success/), где за инциденты наказывают и что нужно больше стараться, чтобы все получилось. Он призвал нас вместо этого создавать "яму успеха," когда по умолчанию вы попадаете в ожидаемое (успешное) действие, и, следовательно, должны сильно постараться, чтобы потерпеть неудачу. +Джефф Этвуд заметил несколько лет назад: языки программирования часто настроены по умолчанию таким образом, что разработчки попадают в "яму отчаяния" (http://blog.codinghorror.com/falling-into-the-pit-of-success/), где за инциденты наказывают и что нужно больше стараться, чтобы все получилось. Он призвал нас вместо этого создавать "яму успеха," когда по умолчанию вы попадаете в ожидаемое (успешное) действие, и, следовательно, должны сильно постараться, чтобы потерпеть неудачу. Обработка ошибок в промисах - несомненно, является дизайном "ямы отчаяния". По умолчанию, он предполагает, что вы хотите, чтобы любые ошибки были поглощены состоянием промиса и если вы забудете исследовать этот состояние, ошибка тихо томится/умирает в безвестности, обычно отчаяния. @@ -1305,7 +1305,7 @@ foo( 42 ) ## Шаблоны промисов -Мы уже увидено неявно шаблон последовательности в цепочках промисов (управление потоком это-затем-это-затем-то), но существует множество вариаций асинхронных шаблонов, которые мы можем построить как абстракции над промисами. Этим шаблоны служат для упрощения выражения асинхронного управления потоком, который помогает сделать наш код более более разумным и более поддерживаемым, даже в самых сложных частях наших программ. +Мы уже видели шаблон последовательности в цепочках промисов (управление потоком это-затем-это-затем-то), но существует множество вариаций асинхронных шаблонов, которые мы можем построить как абстракции над промисами. Этим шаблоны служат для упрощения выражения асинхронного управления потоком, который помогает сделать наш код более более разумным и более поддерживаемым, даже в самых сложных частях наших программ. Два таких шаблона кодируются непосредственно в нативную реализацию ES6 `Promise`, так что мы получаем их бесплатно, чтобы использовать как строительные блоки для других шаблонов. @@ -1315,7 +1315,7 @@ foo( 42 ) В классической терминологии программирования, "шлюз" - это механизм, который ожидает завершения двух или более параллельных/ одновременных задач, прежде чем продолжить работу. Не важно в каком порядке они завершатся, а важно только, что все они должны завершиться чтобы открыть шлюз и позволить потоку управлению потоком идти дальше. -В API промисов, мы называем этот щаблон `all([ .. ])`. +В API промисов, мы называем этот шаблон `all([ .. ])`. Скажем вы хотели сделать два Ajax-запроса в одно и то же время и дождаться окончания обоих, независимо от их порядка, до выполнения третьего Ajax-запроса. Рассмотрим: @@ -1510,7 +1510,7 @@ if (!Promise.first) { ### Одновременные итерации -Иногда вы хотите проходить по списку промисов и выполнить некоторую задачу для них всех, так же, как это можно сделать с синхронными `array`s (e.g., `forEach(..)`, `map(..)`, `some(..)`, and `every(..)`). Если задача Если задача, которую нужно выполнить по отношению к каждому промису, является принципиально синхронной, это отлично работает, точно так же, как мы использовали `forEach(..)` в предыдущем отрывке кода. +Иногда вы хотите проходить по списку промисов и выполнить некоторую задачу для них всех, так же, как это можно сделать с синхронными `array`s (e.g., `forEach(..)`, `map(..)`, `some(..)`, and `every(..)`). Если задача, которую нужно выполнить по отношению к каждому промису, является принципиально синхронной, это отлично работает, точно так же, как мы использовали `forEach(..)` в предыдущем отрывке кода. Но если задачи принципиально асинхронные или могут/должны в противном случае выполняться одновременно, вы можете воспользоваться асинхронными версиями этих функций, предоставляемых многими библиотеками. @@ -1666,7 +1666,7 @@ Promise.all( [p1,p2] ) **Предупреждение:** Будьте осторожны! Если в `Promise.all([ .. ])` будет передает пустой массив, то функция завершится немедленно, а вот `Promise.race([ .. ])` повиснет навсегда и никогда не разрешится. -ES6 `Promise` API довольно простое и понятное. Оно, по крайней мере, достаточно хорош, чтобы обслуживать самые простые случаи асинхронной работы и это хорошее место для начала перестройки вашего кода из ада колбеков в нечто лучшее. +ES6 `Promise` API довольно простое и понятное. Оно, по крайней мере, достаточно хорошо, чтобы обслуживать самые простые случаи асинхронной работы и это хорошее место для начала перестройки вашего кода из ада колбеков в нечто лучшее. Но существует целый ряд асинхронных сложностей, которые часто требуются приложениям и для которых промисы сами по себе будут ограничены в решении. В следующем разделе мы погрузимся в эти ограничения как мотивацию для создания библиотек промисов. @@ -1715,7 +1715,7 @@ p.catch( handleErrors ); Иногда вы можете воспринять это как сигнал о том, что вы можете/должны разложить проблему на два или более промисов. -Представьте, что ув ас есть функция `foo(..)`, которая обеспечивает два значения (`x` и `y`) асинхронно: +Представьте, что у вас есть функция `foo(..)`, которая обеспечивает два значения (`x` и `y`) асинхронно: ```js function getY(x) { @@ -1745,7 +1745,7 @@ foo( 10, 20 ) } ); ``` -Сперва, давайте переставим то. что возвращает `foo(..)` чтобы нам не пришлось упаковывать `x` и `y` в единственное значение `массива`, чтобы для передачи через один промис. Вместо этого мы можем обернуть кажое значение в свой собственный промис: +Сперва, давайте переставим то, что возвращает `foo(..)` чтобы нам не пришлось упаковывать `x` и `y` в единственное значение `массива`, чтобы для передачи через один промис. Вместо этого мы можем обернуть кажое значение в свой собственный промис: ```js function foo(bar,baz) { @@ -2079,7 +2079,7 @@ p.then( function(){ Это ужасно. Это работает, но далеко от идеала. В общем случае следует стараться избегать таких сценариев. -Но если не можете этого избежать, Уродливость этого решения должна быть подсказкой, что *отмена* - это функциональность, которая находится на более высоком уровне абстракции, поверх промисов. Я бы рекомендовал обращаться за помощью к библиотекам абстракций над промисами, а не разрабатывать их самостоятельно. +Но если не можете этого избежать, уродливость этого решения должна быть подсказкой, что *отмена* - это функциональность, которая находится на более высоком уровне абстракции, поверх промисов. Я бы рекомендовал обращаться за помощью к библиотекам абстракций над промисами, а не разрабатывать их самостоятельно. **Примечание** Моя библиотека абстракций над промисами *asynquence* предоставляет именно такую абстракцию и возможность сделать `abort()` для последовательности, все они будут рассмотрены в Приложении А.