You've already forked The-API-Book
mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-08-10 21:51:42 +02:00
shared resources chapter translated
This commit is contained in:
BIN
docs/API.en.epub
BIN
docs/API.en.epub
Binary file not shown.
154
docs/API.en.html
154
docs/API.en.html
File diff suppressed because one or more lines are too long
BIN
docs/API.en.pdf
BIN
docs/API.en.pdf
Binary file not shown.
BIN
docs/API.ru.epub
BIN
docs/API.ru.epub
Binary file not shown.
@@ -6610,7 +6610,7 @@ api.<span class="hljs-title function_">subscribe</span>(
|
||||
</ul>
|
||||
<p><strong>NB</strong>: следует различать Backend-Driven UI и технологию серверного рендеринга (SSR). Второе подразумевает, что одно и то же состояние UI (как правило, в виде HTML-разметки или аналогичного декларативного описания) может быть сгенерировано как сервером, так и клиентом. В SSR, однако, подразумевается, что клиент может интерпретировать ответ сервера и извлекать из него семантические данные.</p>
|
||||
<p>С точки зрения предложения SDK сторонним разработчикам, Backend-Driven UI фактически вынуждает разрабатывать гибридный код (поскольку практика предоставления партнёрам возможности встроиться в функции серверного рендеринга на стороне провайдера API выглядит весьма далёкой от жизнеспособности идеей), и, таким образом, будет страдать от той же проблемы непрозрачности — разработчик не может со своей стороны определить, в каком сейчас состоянии находится визуальный компонент. Как ни странно, этот недостаток одновременно является и достоинством, поскольку вендор API сохраняет возможность на своей стороне производить любые манипуляции с контентом без нарушения обратной совместимости (мы обсудим этот вопрос в главе «<a href="#api-product-lineup">Линейка сервисов API</a>»). Таких клиентских API в мире существует довольно много (в первую очередь, разнообразные виджеты, рекламные API и т.д.), но их сложно назвать полноценными SDK.</p><div class="page-break"></div><h3><a href="#sdk-shared-resources" class="anchor" id="sdk-shared-resources">Глава 47. Разделяемые ресурсы и асинхронные блокировки</a><a href="#chapter-47" class="secondary-anchor" id="chapter-47"> </a></h3>
|
||||
<p>Другой важный паттерн, который мы должны рассмотреть — это доступ к общим ресурсам. Предположим, что в нашем учебном приложении открытие экрана предложения стало требовать выполнения дополнительного запроса к серверу и, таким образом, стало асинхронным. Модифицируем код <code>OfferPanel</code>:</p>
|
||||
<p>Другой важный паттерн, который мы должны рассмотреть — это доступ к общим ресурсам. Предположим, что в нашем учебном приложении открытие экрана предложения стало требовать выполнения дополнительного запроса к серверу и, таким образом, стало асинхронным. Модифицируем код <code>OfferPanelComponent</code>:</p>
|
||||
<pre><code class="language-typescript"><span class="hljs-keyword">class</span> <span class="hljs-title class_">OfferPanelComponent</span> {
|
||||
…
|
||||
show (offer) {
|
||||
@@ -6640,12 +6640,12 @@ api.<span class="hljs-title function_">subscribe</span>(
|
||||
<span class="hljs-title function_">constructor</span> () {
|
||||
…
|
||||
<span class="hljs-variable language_">this</span>.<span class="hljs-property">offerPanel</span>.<span class="hljs-property">events</span>.<span class="hljs-title function_">on</span>(
|
||||
<span class="hljs-string">'beginFullDataLoad'</span>, <span class="hljs-function">() =></span> {
|
||||
<span class="hljs-string">'beginDataLoad'</span>, <span class="hljs-function">() =></span> {
|
||||
<em><span class="hljs-variable language_">this</span>.<span class="hljs-property">isDataLoading</span> = <span class="hljs-literal">true</span>;</em>
|
||||
}
|
||||
);
|
||||
<span class="hljs-variable language_">this</span>.<span class="hljs-property">offerPanel</span>.<span class="hljs-property">events</span>.<span class="hljs-title function_">on</span>(
|
||||
<span class="hljs-string">'endFullDataLoad'</span>, <span class="hljs-function">() =></span> {
|
||||
<span class="hljs-string">'endDataLoad'</span>, <span class="hljs-function">() =></span> {
|
||||
<em><span class="hljs-variable language_">this</span>.<span class="hljs-property">isDataLoading</span> = <span class="hljs-literal">false</span>;</em>
|
||||
}
|
||||
);
|
||||
@@ -6661,11 +6661,15 @@ api.<span class="hljs-title function_">subscribe</span>(
|
||||
</code></pre>
|
||||
<p>Но этот код очень плох по множеству причин:</p>
|
||||
<ul>
|
||||
<li>непонятно, как его модифицировать, если у нас появятся разные виды загрузок данных, причём некоторые из них будут требовать блокировки прокрутки, а некоторые — нет;</li>
|
||||
<li>этот код просто плохо читается: совершенно непонятно, почему события анимации на одном компоненте влияют на прокрутку в другом компоненте;</li>
|
||||
<li>если при выполнении анимации произойдёт какая-то ошибка, событие <code>endAnimation</code> не произойдёт и прокрутка будет заблокирована навсегда.</li>
|
||||
<li>непонятно, как его модифицировать, если у нас появятся разные виды загрузок данных, причём некоторые из них будут требовать блокировки интерфейса, а некоторые — нет;</li>
|
||||
<li>этот код просто плохо читается: совершенно непонятно, почему события загрузки данных на одном компоненте влияют на пользовательскую функциональность в другом компоненте;</li>
|
||||
<li>если при загрузке произойдёт какая-то ошибка, событие <code>endDataLoad</code> не произойдёт, и интерфейс останется заблокированным навсегда.</li>
|
||||
</ul>
|
||||
<p>Если вы внимательно читали предыдущие главы, решение этих двух проблем должен быть очевидным. Необходимо абстрагироваться от самого факта загрузки данных и переформулировать проблемы в высокоуровневых терминах. У нас есть разделяемый ресурс — место на экране. Мы можем показывать в один момент времени только одно предложение. Следовательно, если какому-то актору требуется длящийся доступ к панели, он должен этот доступ явно получить. Отсюда следует, что:</p>
|
||||
<ul>
|
||||
<li>флаг такого доступа должен именоваться явно (например, <code>offerFullViewLocked</code>, а не <code>isDataLoading</code>);</li>
|
||||
<li>флаг контролироваться <code>Composer</code>-ом, но никак не самой панелью предложения (ещё и потому, что подготовка данных для показа — также ответственность <code>Composer</code>-а).</li>
|
||||
</ul>
|
||||
<p>Если вы внимательно читали предыдущие главы, решение этих двух проблем должен быть очевидным. Необходимо абстрагироваться от самого факта анимации и переформулировать проблемы в высокоуровневых терминах. У нас есть разделяемый ресурс — место на экране. Мы можем показывать в один момент времени только одно предложение. Следовательно, если какому-то актору требуется длящийся доступ к панели, он должен этот доступ явно получить. Отсюда следует, что, во-первых, флаг такого доступа должен именоваться явно (например, <code>offerFullViewLocked</code>, а не <code>isDataLoading</code>) и контролироваться <code>Composer</code>-ом, но никак не самой панелью предложения (ещё и потому, что подготовка данных для показа — также ответственность <code>Composer</code>-а).</p>
|
||||
<pre><code class="language-typescript"><span class="hljs-keyword">class</span> <span class="hljs-title class_">SearchBoxComposer</span> {
|
||||
<span class="hljs-title function_">constructor</span> () {
|
||||
…
|
||||
@@ -6718,7 +6722,7 @@ api.<span class="hljs-title function_">subscribe</span>(
|
||||
}
|
||||
}
|
||||
</code></pre>
|
||||
<p><strong>NB</strong>: вторым параметром в <code>acquireLock</code> мы передали максимальное время жизни блокировки — 10 секунд. Если в течение этого времени блокировка не снята (например, в случае, если ), она будет отменена автоматически.</p>
|
||||
<p><strong>NB</strong>: вторым параметром в <code>acquireLock</code> мы передали максимальное время жизни блокировки — 10 секунд. Если в течение этого времени блокировка не снята (например, в случае, если мы забыли обработать какое-то исключение или выставить таймаут на запрос к серверу), она будет отменена автоматически, и интерфейс будет разблокирован.</p>
|
||||
<p>В таком подходе мы можем реализовать не только блокировки, но и различные сценарии, которые позволяют нам более гибко ими управлять. Добавим в функцию захвата ресурса дополнительные данные о целях захвата:</p>
|
||||
<pre><code class="language-typescript">lock = <span class="hljs-variable language_">this</span>.<span class="hljs-title function_">acquireLock</span>(
|
||||
<span class="hljs-string">'offerFullView'</span>, <span class="hljs-string">'10s'</span>, {
|
||||
@@ -6730,7 +6734,7 @@ api.<span class="hljs-title function_">subscribe</span>(
|
||||
}
|
||||
);
|
||||
</code></pre>
|
||||
<p>Тогда текущий владелец ресурса (или диспетчер блокировок) может, в зависимости от ситуации, отдавать владение ресурсом или, наоборот, запрещать перехват. Скажем, если открытие панели инициировано программистом через вызов API компонента (а не пользователем через выбор предложения в списке), оно может иметь более высокий приоритет и быть разрешено:</p>
|
||||
<p>Тогда текущий владелец ресурса (или диспетчер блокировок, если мы реализуем такой объект) может, в зависимости от ситуации, отдавать владение ресурсом или, наоборот, запрещать перехват. Скажем, если открытие панели инициировано программистом через вызов API компонента (а не пользователем через выбор предложения в списке), оно может иметь более высокий приоритет и быть разрешено:</p>
|
||||
<pre><code class="language-typescript">lock.<span class="hljs-property">events</span>.<span class="hljs-title function_">on</span>(<span class="hljs-string">'tryAcquire'</span>, <span class="hljs-function">(<span class="hljs-params">actor</span>) =></span> {
|
||||
<span class="hljs-keyword">if</span> (sender.<span class="hljs-property">reason</span> == <span class="hljs-string">'apiSelectOffer'</span>) {
|
||||
lock.<span class="hljs-title function_">release</span>();
|
||||
@@ -6742,7 +6746,7 @@ api.<span class="hljs-title function_">subscribe</span>(
|
||||
</code></pre>
|
||||
<p>Дополнительно мы можем ввести и обработку потери контроля ресурса — например, отменить загрузку данных, которые больше не нужны.</p>
|
||||
<pre><code class="language-typescript">lock.<span class="hljs-property">events</span>.<span class="hljs-title function_">on</span>(<span class="hljs-string">'lost'</span>, <span class="hljs-function">() =></span> {
|
||||
<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">cancelFullDataLoad</span>();
|
||||
<span class="hljs-variable language_">this</span>.<span class="hljs-title function_">cancelFullOfferDataLoad</span>();
|
||||
})
|
||||
</code></pre>
|
||||
<p>Паттерн контроля разделяемых ресурсов также хорошо сочетается с паттерном «модель»: акторы могут захватывать доступ на чтение и/или изменение свойств или групп свойств модели.</p>
|
||||
@@ -6750,9 +6754,9 @@ api.<span class="hljs-title function_">subscribe</span>(
|
||||
<ul>
|
||||
<li>открыть панель предложения;</li>
|
||||
<li>вместо настоящих данных отобразить спиннер или какую-то другую индикацию загрузки;</li>
|
||||
<li>асинхронно обновить отображение при получении ответа от сервера.
|
||||
Однако в постановке проблемы это ничего не меняет: нам всё ещё нужно разработать политику разрешения конфликтов для случая, если какой-то актор пытается открыть панель предложения, пока загрузка данных ещё не закончена, для чего нам вновь нужны разделяемые ресурсы и их захват.</li>
|
||||
<li>асинхронно обновить отображение при получении ответа от сервера.</li>
|
||||
</ul>
|
||||
<p>Однако в постановке проблемы это ничего не меняет: нам всё ещё нужно разработать политику разрешения конфликтов для случая, если какой-то актор пытается открыть панель предложения, пока загрузка данных ещё не закончена, для чего нам вновь нужны разделяемые ресурсы и их захват.</p>
|
||||
<p>Отметим, что в современном фронтенде (к нашему большому сожалению) подобные упражнения с захватом контроля на время загрузки данных или анимации компонентов практически не производятся (считается, что такие асинхронные операции происходят быстро, и коллизии доступа не представляют собой проблемы). Однако, если асинхронные операции выполняются долго (происходят длительные или многоступенчатые загрузки данных, сложные анимации), пренебрежение организацией доступа может быть очень серьёзной UX-проблемой.</p><div class="page-break"></div><h3><a href="#chapter-48" class="anchor" id="chapter-48">Глава 48. Вычисляемые свойства</a></h3><div class="page-break"></div><h3><a href="#chapter-49" class="anchor" id="chapter-49">Глава 49. В заключение</a></h3><div class="page-break"></div><h2><a href="#section-7" class="anchor" id="section-7">Раздел VI. API как продукт</a></h2><h3><a href="#api-product" class="anchor" id="api-product">Глава 50. Продукт API</a><a href="#chapter-50" class="secondary-anchor" id="chapter-50"> </a></h3>
|
||||
<p>Когда мы говорим об API как о продукте, необходимо чётко зафиксировать два важных тезиса.</p>
|
||||
<ol>
|
||||
|
BIN
docs/API.ru.pdf
BIN
docs/API.ru.pdf
Binary file not shown.
@@ -131,7 +131,7 @@
|
||||
<li><a href="API.en.html#sdk-decomposing">Chapter 44. Decomposing UI Components</a></li>
|
||||
<li><a href="API.en.html#sdk-mv-frameworks">Chapter 45. The MV* Frameworks</a></li>
|
||||
<li><a href="API.en.html#sdk-backend-driven">Chapter 46. The Backend-Driven UI</a></li>
|
||||
<li><a href="API.en.html#chapter-47">Chapter 47. Shared Resources and Asynchronous Locks</a></li>
|
||||
<li><a href="API.en.html#sdk-shared-resources">Chapter 47. Shared Resources and Asynchronous Locks</a></li>
|
||||
<li><a href="API.en.html#chapter-48">Chapter 48. Computed Properties</a></li>
|
||||
<li><a href="API.en.html#chapter-49">Chapter 49. Conclusion</a></li>
|
||||
</ul>
|
||||
|
Reference in New Issue
Block a user