tgoop.com/super_oleg_dev/131
Last Update:
Переходим к телепортации промисов с сервера на клиент.
В первую очередь, мне очень комфортно работать с паттерном Deferred и он отлично подходит для этой задачи:
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.resolve = (data) => {
this.resolveData = data;
resolve(data);
};
this.reject = (reason) => {
this.rejectReason = reason;
reject(reason);
};
});
}
isResolved() {
return typeof this.resolveData !== 'undefined';
}
isRejected() {
return typeof this.rejectReason !== 'undefined';
}
}
На сервере, на каждое асинхронное действие, которое мы пометим как отложенное, необходимо создать свой экземпляр Deferred промиса, и выполнить его resolve/reject по завершению соответствующей асинхронной задачи.
Этот паттерн позволит не ждать сайд-эффект сразу, но подписаться на его выполнение позже, псевдо-код:
async function runDeferredAction() {
deferred = new Deferred()
// допустим этот асинхронный метод выполняется 5 секунд
someLongAction
.then(deferred.resolve)
.catch(deferred.reject)
}
async function render() {
// запускаем сайд эффекты, runDeferredAction резолвится сразу и не блокирует ответ
await promiseAll([
runAnyAction(),
runAnyAction(),
runDeferredAction()
])
// запускаем рендер в стрим
reactRenderToStream()
// ждем отложенный экшен
await deferred.promise
// телепортируем промис
...
// закрываем стрим
closeStream()
}
В качестве механизма для отложенной загрузки данных показалось хорошим решением использовать уже существующие трамвайные Actions - наш основной инструмент для создания и выполнения сайд-эффектов.
В отличие от функций - загрузчиков данных в Remix или SvelteKit, на одну страницу трамвай позволяет запустить множество параллельных экшенов, также мы обычно не используем данные из экшенов напрямую, а передаем их с сервера на клиент через трамвайные сторы.
Для новых deferred экшенов оказалось проще все-таки использовать их напрямую для получения данных, что бы не усложнять “телепортацию” и не добавлять новых API (еще раздумываю над этим решением).
Итак, у нас есть набор страничных экшенов, некоторые из них помечены как deferred
, опишу механизм телепортации:
- трамвай не ждет выполнения deferred экшенов перед рендерингом страницы на сервере
- на каждый такой экшен создается инстанс new Deferred()
, который будет зарезолвлен после резолва самого экшена
- на клиент в head передается инлайн скрипт вида <script>window.DEFERRED_MAP['actionName'] = new Deferred()</script>
- то есть название экшена и становится уникальным ключем и таким “мостом” между сервером и клиентом
- после резолва экшена, на клиент передается инлайн скрипт с резолвом промиса с соответствующими данными (их обернул в защиту от XSS атак) - <script>window.DEFERRED_MAP['actionName'].resolve(SANITIZED_ACTION_DATA)</script>
Таким образом, промис на клиенте будет зарезолвлен практически сразу после того как это произойдет на сервере.
BY SuperOleg dev notes
Share with your friend now:
tgoop.com/super_oleg_dev/131