Redux — это общая библиотека для управления состоянием переднего плана. Она не только широко используется в приложении React, но и повсеместно встречается в Wepy, Flutter и других фреймворках. идея Редукса.
Flux
FLUX — это базовая архитектура Facebook для создания клиентских веб-приложений.Мы можем рассматривать FLUX как шаблон проектирования потоков данных в приложениях, а Redux — это решение, основанное на основной идее, основанной на FLUX.Также получил подтверждение оригинального автора.
Сначала во Flux появятся следующие роли:
- Диспетчер: Диспетчер, который получает Действия и отправляет их в Магазин.
- Действие: сообщение о действии, включая тип действия и описание действия.
- Магазин: центр обработки данных, в котором хранятся данные приложений и который отвечает на сообщения о действиях.
- Представление: представление приложения, которое отображает данные Магазина и реагирует на обновления Магазина в режиме реального времени.
С точки зрения коммуникации это также можно рассматривать какAction请求层 -> Dispatcher传输层 -> Store处理层 -> View视图层
.
Однонаправленный поток данных
Данные в приложениях Flux передаются в одном направлении:
- Представление генерирует сообщение, и действие передается планировщику.
- Планировщик отправляет сообщения о действиях в каждый центр обработки данных.
- Затем центр обработки данных передает данные в представление.
Однонаправленный поток данных также имеет следующие характеристики:
- Централизованное управление данными. Обычные приложения могут изменять и сохранять состояние данных в любом месте на уровне представления или в обратных вызовах, в то время как в архитектуре Flux все данные хранятся и управляются только в Store.
- предсказуемость. При двусторонней привязке или реактивном программировании изменение одного объекта может привести к изменению другого объекта, что инициирует несколько каскадных обновлений. Для архитектуры Flux триггер Action может вызвать только цикл потока данных, что делает данные более предсказуемыми.
- Легко отслеживать изменения. Все причины изменения данных можно описать с помощью Action, а Action — это просто чистый объект, поэтому его очень легко сериализовать или просмотреть.
Рабочий процесс для Flux
Из приведенных выше глав мы, вероятно, знаем обязанности каждой роли в Flux, поэтому сейчас мы объясним, как они формируют весь рабочий процесс, на простом примере кода:
На картинке выше естьAction Creator
Концепция, на самом деле, они используются для помощи в создании объекта действия и передачи диспетчеру:
function addTodo(desc) {
const action = {
type: 'ADD_TODO',
payload: {
id: Date.now(),
done: false,
desciption: desc
}
}
dispatcher(action)
}
Здесь я все же надеюсь сделать простое описание в виде кода, так будет нагляднее, сначала инициализируем проект:
mkdir flux-demo && cd flux-demo
npm init -y && npm i react flux
touch index.js
Затем мы создаем объект Dispatcher, который по сути представляет собой систему событий в системе Flux, используемую для запуска событий и ответа на обратные вызовы, и в Flux есть только один глобальный объект Dispatcher:
import { Dispatcher } from 'flux';
const TodoDispatcher = new Dispatcher();
Затем зарегистрируйте Store и ответьте на метод Action:
import { ReduceStore } from 'flux/utils';
class TodoStore extends ReduceStore {
constructor() {
super(TodoDispatcher);
}
getInitialState() {
return [];
}
reduce(state, action) {
switch (action.type) {
case 'ADD_TODO':
return state.concat(action.payload);
default:
return state;
}
}
}
const TodoStore = new TodoStore();
В конструкторе магазина будетTodoDispatcher
Переданный вызову родительского конструктора, он фактически вызывается в Dispatcher.register
метод регистрирует Store какdispatch
Метод обратного вызова используется для ответа на каждый объект Action.
В случае почти примера FLUX есть вид подключения. Когда Store изменяется, запускается событие CHANGE и обновляется слой представления, ниже приведен полный код:
const { Dispatcher } = require('flux');
const { ReduceStore } = require('flux/utils');
// Dispatcher
const TodoDispatcher = new Dispatcher();
// Action Types
const ADD_TODO = 'ADD_TODO';
// Action Creator
function addTodo(desc) {
const action = {
type: 'ADD_TODO',
payload: {
id: Date.now(),
done: false,
desciption: desc
}
};
TodoDispatcher.dispatch(action);
}
// Store
class TodoStore extends ReduceStore {
constructor() {
super(TodoDispatcher);
}
getInitialState() {
return [];
}
reduce(state, action) {
switch (action.type) {
case ADD_TODO:
return state.concat(action.payload);
default:
return state;
}
}
}
const todoStore = new TodoStore();
console.log(todoStore.getState()); // []
addTodo('早晨起来,拥抱太阳');
console.log(todoStore.getState()); // [ { id: 1553392929453, done: false, desciption: '早晨起来,拥抱太阳' } ]
Поток и реакция
Такие архитектурные решения, как Flux, появились очень давно, но почему они стали популярными только в последние годы? Я думаю, что большая часть этого фактора зависит от появления фреймворка React именно потому, что виртуальный DOM React сделал управление данными мейнстримом в сочетании с эффективнымReact diff
Так что такая структура существует более разумно:
В структуре верхнего уровня, близкой к представлению, есть специальный слой представления, здесь мы называем его контроллером представления (View Controller), который используется для получения данных из Store и передачи данных слою представления и его потомкам. и отвечает за прослушивание событий изменения данных в Store.
При получении события сначала контроллер представления получит последние данные из Store и вызовет свой собственныйsetState
илиforceUpdate
Функции, которые запускают рендеринг представления и методы повторного рендеринга всех потомков.
Обычно мы будем передавать весь объект Store на верхний уровень цепочки View, а затем родительский узел View будет передавать его в Store данные, требуемые потомками по очереди, что может гарантировать, что компоненты потомков более функциональным и уменьшит количество представлений контроллера, предназначенных для повышения производительности.
Redux
Redux — это контейнер управления предсказуемым состоянием для приложений JavaScript, который имеет следующие функции:
- Предсказуемость, использование Redux может помочь вам писать программы, которые ведут себя последовательно в разных средах и легко тестируются.
- Централизация, централизованное управление состоянием приложений позволяет легко реализовать отзыв, восстановление, сохранение состояния и т. д.
- Настраиваемый Redux Devtools предоставляет мощную функцию отслеживания состояния, которая может быть очень удобной для путешественника во времени.
- Гибкий Redux работает с любым уровнем пользовательского интерфейса и имеет огромную экосистему.
Он также имеет три принципа:
- Единый источник данных. Состояние всего приложения хранится в дереве объектов единого Магазина.
- Состояние состояния доступно только для чтения. Вы не должны изменять состояние напрямую, а должны вызывать действие для его изменения. Действие — это обычный объект, поэтому его можно распечатать, сериализовать и сохранить.
- Используйте чистые функции для изменения состояния. Чтобы указать, как состояние преобразуется с помощью операции Action, необходимо написать редукторам чистые функции для обработки. Редюсеры работают с текущим деревом состояний и действиями, каждый раз возвращая новый объект состояния.
Отличие от потока
Redux вдохновлен архитектурой Flux, но имеет несколько отличий в реализации:
- В Redux нет диспетчера. Он использует чистые функции для замены обработчиков событий и не требует дополнительных сущностей для управления ими. Поток описывается как:
(state, action) => state
, и чистые функции также реализуют эту идею. - Redux — это неизменяемый набор данных. Вместо того, чтобы вносить изменения в текущее состояние, Redux генерирует новый объект для обновления состояния после запуска каждого запроса действия.
- Redux имеет один и только один объект Store. В его Store хранится состояние всего приложения.
Action
В Redux Action — это чистый объект JavaScript, который используется для описания информации об изменении данных в Store, а также является источником информации для Store.Короче говоря, все изменения данных происходят из Actions.
В объекте Action должно быть полеtype
Он используется для описания типа действия, а их значение представляет собой строковый тип.Обычно я буду хранить типы типов всех действий в одном файле для удобства обслуживания (вам не нужно делать это для небольших проектов):
// store/mutation-types.js
export const ADD_TODO = 'ADD_TODO'
export const REMOVE_TODO = 'REMOVE_TODO'
// store/actions.js
import * as types from './mutation-types.js'
export function addItem(item) {
return {
type: types.ADD_TODO,
// .. pass item
}
}
Помимо типа объекта Action, теоретически другие структуры данных можно настроить под себя, что и рекомендуется здесьflux-standard-actionКороче говоря, этот стандарт Flux Action определяет базовую информацию о структуре объекта Action:
{
type: 'ADD_TODO',
payload: {
text: 'Do something.'
}
}
Также есть Действия для индикации ошибок:
{
type: 'ADD_TODO',
payload: new Error(),
error: true
}
При построении Action нам нужно сделать так, чтобы объект Action нес как можно меньше данных, например, весь объект можно заменить, передав id.
Action Creator
Мы отличаем Action Creator от Action, чтобы не путать их. В Redux Action Creator — это функция для создания действий, которая возвращает объект Action:
function addTodo(text) {
return {
type: 'ADD_TODO',
payload: {
text,
}
}
}
а такжеFlux
Разница в том, что во Flux Action Creator также отвечает за запуск операций отправки, тогда как Redux отвечает только за создание действий, а фактические операции отправки определяютсяstore.dispatch
Выполнение метода:store.dispatch(addTodo('something'))
, что делает поведение Action Creator более простым и легким для тестирования.
bindActionCreators
Обычно мы не используем его напрямуюstore.dispatch
Метод отправляет действие, но использует метод подключения для его получения.dispatch
диспетчер и использоватьbindActionCreators
Автоматически привязывать Action Creators к функциям отправки:
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
function mapDispatchToProps(dispatch) {
return bindActionCreators(
{ addTodo },
dispatch
);
}
const Todo = ({ addTodo }) => {}
export default connect(null, mapDispatchToProps)(Todo);
пройти черезbindActionCreators
После этого мы можем передать эти Action Creators в подкомпоненты, а подкомпоненты не должны их получатьdispatch
метод, но вызовите метод напрямую, чтобы вызвать действие.
Reducers
Для действий они просто описывают, что произошло, а все изменения в состоянии приложения изменяются с помощью редукторов.
Перед реализацией функции Reducer сначала нужно определить структуру данных State в приложении, которое хранится в виде отдельного объекта, поэтому при его проектировании старайтесь рассматривать его с глобальной точки зрения и логично делить на разные модули свернуты, избегают вложенности и хранят данные отдельно от состояния пользовательского интерфейса.
Редюсер — это чистая функция, которая объединяет предыдущее состояние с объектом Action для создания нового дерева состояний приложения:
(previousState, action) => newState
Внутренний общий проходswitch...case
Заявления для обработки различных действий.
Очень важно поддерживать чистую функциональную природу Редьюсера.Редуктор должен делать следующее:
- Исходное состояние не должно изменяться напрямую, а порождать новое состояние на основе исходного состояния.
- При вызове не должно быть никаких побочных эффектов, таких как вызовы API, скачки маршрутизации и т. д.
- При передаче одних и тех же параметров возвращаемый результат каждого вызова должен быть одинаковым, поэтому избегайте использования
Date.now()
илиMath.random()
такая нечистая функция.
combineReducers
Наиболее распространенной формой состояния для приложений Redux является простой объект Javascript, который содержит «фрагменты» данных, специфичных для домена, для каждого ключа верхнего уровня, каждый «фрагмент» имеет идентично структурированную функцию-редуктор, которая обрабатывает данные для этого домена. Обновления, несколько редьюсеры также могут одновременно реагировать на одно и то же действие, независимо обновляя свое состояние при необходимости.
Именно потому, что этот шаблон настолько распространен, Redux предоставляет инструмент для реализации этого поведения:combineReducers
. Он используется только для упрощения наиболее распространенного примера редукторов Redux, и избежать некоторых распространенных проблем. Он также имеет функцию, когда действие генерируется, он будет выполнен редуктор, каждый ломтик предоставляет обновление статуса для ломтика возможностей. Традиционный единственный редуктор не может этого сделать, поэтому может быть выполнен только один раз в корне функции редуктора.
Функция редуктора будет действовать какcreateStore
, и при первом вызове редьюсераstate
Параметрыundefined
, Так что нам также нужен метод инициализации State. Например:
const initialState = { count: 0 }
functino reducer(state = initialState, action) {
switch (action.type) {
case: 'INCREMENT':
return { count: state.count + 1 }
case: 'DECREMENT':
return { count: state.count - 1 }
default:
return state;
}
}
В обычных приложениях различные состояния хранятся в State, что затрудняет быстрое сопровождение одной функции Reducer:
...
case: 'LOADING':
...
case: 'UI_DISPLAY':
...
...
Поэтому наша основная цель состоит в том, чтобы разделить функцию как можно короче и соответствовать принципу единой ответственности, который не только легко поддерживать, но и легко расширять.Далее давайте рассмотрим простой пример TODO:
const initialState = {
visibilityFilter: 'SHOW_ALL',
todos: []
}
function appReducer(state = initialState, action) {
switch (action.type) {
case 'SET_VISIBILITY_FILTER': {
return Object.assign({}, state, {
visibilityFilter: action.filter
})
}
case 'ADD_TODO': {
return Object.assign({}, state, {
todos: state.todos.concat({
id: action.id,
text: action.text,
completed: false
})
})
}
default:
return state
}
}
Эта функция включает в себя две отдельные внутренние логики: Поле фильтра задается с помощью логики манипулирования объектом TODO, если оно продолжит работать в расширенном режиме, функция редуктора сделает его большим и растущим, поэтому нам нужно, чтобы эти два логических разделения открывали отдельное обслуживание:
function appReducer(state = initialState, action) {
return {
todos: todosReducer(state.todos, action),
visibilityFilter: visibilityReducer(state.visibilityFilter, action)
}
}
function todosReducer(todosState = [], action) {
switch (action.type) {
case 'ADD_TODO': {
return Object.assign({}, state, {
todos: state.todos.concat({
id: action.id,
text: action.text,
completed: false
})
})
}
default:
return todosState
}
}
function visibilityReducer(visibilityState = 'SHOW_ALL', action) {
switch (action.type) {
case 'SET_VISIBILITY_FILTER':
return setVisibilityFilter(visibilityState, action)
default:
return visibilityState
}
}
Мы разделяем весь объект Reducer на две части, и они независимо поддерживают свою часть состояния, этот шаблон проектирования делает весь Reducer разбросанным по независимым слайсам. Redux имеет встроенныйcombineReducers
Вспомогательная функция, которая побуждает нас нарезать редюсер верхнего уровня следующим образом, который организует все срезы в новую функцию редуктора:
const rootReducer = combineReducers({
todos: todosReducer,
visibilityFilter: visibilityReducer
})
В объекте состояния, возвращаемом combReducers, каждое имя ключа представляет имя ключа дочернего редуктора, когда оно передается, и они служат пространством имен состояния в дочернем редюсере.
Store
В приложении Redux есть только одно хранилище, черезcreateStore
создавать. Объект Store используется для объединения действий с редьюсерами и имеет следующие обязанности:
- Сохраните состояние приложения и разрешите ему пройти
getState()
Состояние доступа к методу. - поставка
dispatch(action)
Метод отправляет действие в функцию Reducer для обновления состояния. - пройти через
subscribe(listener)
Прислушивайтесь к изменениям состояния.
дляsubscribe
Другими словами, каждый вызовdispatch
После метода он сработает, в это время часть дерева состояний могла измениться, мы можем использовать это в callback-функции метода подписки.getState
илиdispatch
метод, но использовать его нужно с осторожностью.subscribe
Также возвращает функцию после вызоваunsubscribe
Функция отписки.
Redux Middleware
Что касается концепции промежуточного программного обеспечения, я считаю, что у всех есть определенное понимание этой концепции через другие приложения.Для Redux, когда мы говорим о промежуточном программном обеспечении, мы часто имеем в виду период с момента, когда действие инициируется, до тех пор, пока оно не достигнет редьюсера. Redux предоставляет возможность расширения сторонней программы через механизм Middleware.
Чтобы лучше проиллюстрировать промежуточное ПО, я сначала инициализирую минимальный экземпляр с помощью Redux:
const { createStore } = require('redux');
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
function reducer(state = 0, action) {
switch (action.type) {
case INCREMENT:
return state + 1;
case DECREMENT:
throw new Error('decrement error');
default:
return state;
}
}
void function main() {
const store = createStore(reducer);
store.dispatch({ type: INCREMENT });
console.log(store.getState()); // 打印 1
}()
Шаг 1. Вручную добавьте промежуточное ПО журнала печати
Чтобы иметь глубокое представление о промежуточном программном обеспечении Redux, давайте шаг за шагом реализуем функцию с функцией промежуточного программного обеспечения. Чтобы отслеживать изменения состояния программы, нам может потребоваться реализовать промежуточный программный механизм печати журнала для печати изменений действия и состояния после выполнения. Мы сначала проходимstore
объект создаетlogger
объект, вdispatch
Перед и после печатающего журнала:
void (function main() {
const store = createStore(reducer);
const logger = loggerMiddleware(store);
logger({ type: INCREMENT });
function loggerMiddleware(store) {
return action => {
console.log('dispatching', action);
let result = store.dispatch(action);
console.log('next state', store.getState());
return result;
};
}
})();
// 程序运行结果
dispatching { type: 'INCREMENT' }
next state 1
Шаг 2. Добавьте еще одно промежуточное ПО, которое печатает неправильно
Чтобы отслеживать состояние приложения, нам также необходимо реализовать промежуточное программное обеспечение, когда приложениеdispatch
Когда в процессе возникает ошибка, middleware может зафиксировать ошибку и вовремя сообщить о ней (обычно об этом можно сообщить Sentry, но здесь она просто распечатывается):
void (function main() {
const store = createStore(reducer);
const crasher = crashMiddleware(store);
crasher({ type: DECREMENT });
function crashMiddleware(store) {
return action => {
try {
return dispatch(action);
} catch (err) {
console.error('Caught an exception!', err);
}
};
}
})();
После выполнения программы можно увидеть правильный захват функции в командной строкеDECREMENT中的错误
:
Caught an exception! ReferenceError: dispatch is not defined
Шаг 3. Соедините два промежуточных слоя вместе
Обычно в приложении используется несколько промежуточных программ, и объединение различных промежуточных программ вместе является очень важным шагом.Koa2
Исходный код , вы, вероятно, знаете что-то под названиемcompose
функция, которая будет отвечать за обработку каскадной работы промежуточного программного обеспечения.
Здесь, чтобы понять принцип, мы пошагово разбираем. Первые две основные задачи, которые будут выполнять методы упаковки слоев промежуточного программного обеспечения Dispatch, поэтому нам просто нужно обернуть слои диспетчерского и входящего вызовов самым глубоким промежуточным программным обеспечением, которое может соответствовать нашей программе. Требуются:
dispatch = store.dispatch
↓↓↓
// 没有中间件的情况
dispatch(action)
↓↓↓
// 当添加上LoggerMiddleware
LoggerDispatch = action => {
// LoggerMiddleware TODO
dispatch(action)
// LoggerMiddleware TODO
}
dispatch(action)
↓↓↓
// 当添加上CrashMiddleware
CrashDispatch = action => {
// CrashMiddleware TODO
LoggerDispatch(action)
// CrashMiddleware TODO
}
Если вы знакомы с использованием функций высшего порядка, я полагаю, что приведенные выше идеи нетрудны для понимания.Давайте изменим исходный код и попробуем посмотреть, могут ли два промежуточных программного обеспечения работать правильно таким образом:
void function main() {
const store = createStore(reducer);
let dispatch = store.dispatch
dispatch = loggerMiddleware(store)(dispatch)
dispatch = crashMiddleware(store)(dispatch)
dispatch({ type: INCREMENT });
dispatch({ type: DECREMENT });
function loggerMiddleware(store) {
return dispatch => {
return action => {
console.log('dispatching', action);
let result = dispatch(action);
console.log('next state', store.getState());
return result;
};
};
}
function crashMiddleware(store) {
return dispatch => {
return action => {
try {
return dispatch(action);
} catch (err) {
console.error('Caught an exception!', err);
}
};
};
}
}();
На данный момент результат печати (как и ожидалось):
dispatching { type: 'INCREMENT' }
next state 1
dispatching { type: 'DECREMENT' }
Caught an exception! Error: decrement error
Конечно, мы хотим генерировать и вызывать диспетчеризацию более элегантным способом, я бы ожидал, что при создании передается массив промежуточного программного обеспечения для генерацииStore
Объект:
// 简单实现
function createStoreWithMiddleware(reducer, middlewares) {
const store = createStore(reducer);
let dispatch = store.dispatch;
middlewares.forEach(middleware => {
dispatch = middleware(store)(dispatch);
});
return Object.assign({}, store, { dispatch });
}
void function main() {
const middlewares = [loggerMiddleware, crashMiddleware];
const store = createStoreWithMiddleware(reducer, middlewares);
store.dispatch({ type: INCREMENT });
store.dispatch({ type: DECREMENT });
// ...
}()
Step 4. back to Redux
Изучив шаги 1–3, мы, вероятно, реализовали механизм промежуточного программного обеспечения Redux в соответствии с тыквой, а теперь давайте посмотрим на интерфейс промежуточного программного обеспечения, предоставляемый самим Redux.
существуетcreateStore
Метод, поддерживаетenhancer
Параметры, означает трехстороннее расширение, в настоящее время поддерживается только расширеннымapplyMiddleware
Промежуточное ПО, созданное методом.
applyMiddleware поддерживает передачу нескольких совпаденийRedux middleware API
Промежуточное ПО, каждое промежуточное ПО имеет форму:({ dispatch, getState }) => next => action
. Сделаем небольшую модификацию и реализуем через applyMiddleware и интерфейс createStore (нужно только изменить этапы создания магазина):
// ...
const middlewares = [loggerMiddleware, crashMiddleware];
const store = createStore(reducer, applyMiddleware(...middlewares));
// ...
Используйте метод applyMiddleware, чтобы объединить несколько компонентов промежуточного слоя, чтобы сформировать цепочку ПО промежуточного слоя. Среди них каждому промежуточному программному обеспечению не нужно заботиться о какой-либо информации о промежуточном программном обеспечении до и после него в цепочке. Наиболее распространенный сценарий для ПО промежуточного слоя — реализация методов асинхронных действий, таких какredux-thunk
а такжеredux-saga
.
Асинхронное действие
Для стандартного приложения Redux мы можем выполнять синхронные обновления только путем отправки действий.Чтобы добиться возможности асинхронной отправки, официальной стандартной практикой является использование промежуточного программного обеспечения redux-thunk.
Чтобы понять, что такое redux-thunk, вспомним представленный выше API Middleware:({ dispatch, getState }) => next => action
, благодаря гибкому механизму промежуточного программного обеспечения, он предоставляет redux-thunk с возможностью откладывать отправку действий, позволяя людям писать Action Creator вместо немедленного возврата объекта Action, но возвращая функцию для асинхронного планирования, поэтому он вызывается дляAsync Action Creator
:
// synchronous, Action Creator
function increment() {
return {
type: 'INCREMENT'
}
}
// asynchronous, Async Action Creator
function incrementAsync() {
return dispatch => {
setTimeout(() => dispatch({ type: 'INCREMENT' }), 1000)
}
}
Исходный код redux-thunk составляет всего около 10 строк:
function createThunkMiddleware(extraArgument) {
return ({ dispatch, getState }) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState, extraArgument);
}
return next(action);
};
}
const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;
export default thunk;
пройти черезdispatch(ActionCreator())
При вызове функция определяет тип параметра:
- Если это объект, выполните обычный процесс запуска и отправьте действие напрямую.
- Если это функция, она рассматривается как создатель асинхронного действия, а метод отправки и метод getState вводятся в качестве параметров.Если withExtraArgument зарегистрирован глобально, он также будет передан в качестве третьего параметра.
Что касается того, почему это называется «thunk», оно происходит от «думать»,i становится u, что означает передачу абсолютной власти от меня к вам, что, на мой взгляд, является лучшим объяснением. Если вы хотите отследить источник, это на самом деле шаблон «стратегии оценки», то есть когда следует оценивать параметры функции, такие как функция:
function test(y) { return y + 1 }
const x = 1;
test(x + 1);
На данный момент есть два спорных момента:
- Позвоните по значению, то есть перед входом в функциональный корпус, рассчитайте
x + 1 = 2
, а затем передать значение в функцию; - Вызывать по имени, то есть напрямую вызывать выражение
x + 1
Передайте функцию и вычислите значение выражения, когда это необходимо.
Обычно реализация компилятором «вызова по имени» обычно помещает параметры во временную функцию, а затем передает временную функцию в тело функции, и эта функция называется Thunk.Если вызов по имени принят, вышеприведенная функция вызов будет преобразован в форму параметра Thunk:
const thunk = () => (x + 1)
function test(thunk) {
return thunk() + 1;
}