1
0
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:
Sergey Konstantinov
2023-09-22 09:26:18 +03:00
parent 40dab74771
commit 25f163013f
9 changed files with 353 additions and 27 deletions

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View File

@@ -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>

Binary file not shown.

View File

@@ -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>