Skip to content

Batching โ€‹

Batching allows to invoke reactions/effects โ€” only once, for changes that were made almost at the same time.

INFO

Functions provided to subscribe, autorun, or observer are collectively known as Reactions/Effects โ€” they react to changes in observables and re-execute when dependencies are updated.

How it works โ€‹

Letโ€™s make some preparations before we get into how it works.

ts
import { Observable, listen, subscribe } from 'kr-observable'

class State extends Observable {
  a = 1
  b = 1
  c = 1
}

const state = new State()

// should be invoked when a, b or c changes
function subscriber() {
  console.log('run reaction ๐Ÿ‘Œ')
}

// will log all changes is state
function listener(property) {
  console.log(property, ' where changed')
}

listen(state, listener)
subscribe(state, subscriber, new Set(['a', 'b', 'c']))

// will emulate async requests
function delay() {
  return new Promise((resolve) => {
    setTimeout(() => resolve(2), 50)
  })
}

Now weโ€™ll change state in different ways to understand and control ยซbatchingยป.

INFO

Here changes are made at same time, and reaction will run once:

ts
function change() {
  state.a = 2
  state.b = 2
  state.c = 2
}
change()

Console output:

text
a were changed
b were changed
c were changed
run reaction ๐Ÿ‘Œ

INFO

Here changes are made in an async function with await statement, but since changes starts after promise is resolved, they are made at same time, and effect will run once:

ts
async function change() {
  state.a = await delay()
  state.b = 2
  state.c = 2
}
change()

Console output:

text
a were changed
b were changed
c were changed
run reaction ๐Ÿ‘Œ

INFO

Here changes are made in an async function with more than one await statement during assignment:

ts
async function change() {
  state.a = await delay()
  state.b = await delay()
  state.c = 2
}
change()

Console output:

text
a were changed
๐Ÿ”ฅ๐Ÿ”ฅ run reaction ๐Ÿ‘Œ
b were changed
c were changed
run reaction ๐Ÿ‘Œ

Why effect was invoked twice in last example?

Thatโ€™s because assignment value to property b, was deferred by await statement to the next tick, but changes are considered made at the same time, if they happen in the same tick.

Control batching โ€‹

Now that we understand how batching works, we can easily control it. We just wait until promises resolves, and then make changes:

ts
async function change() {
  const a = await delay()
  const b = await delay()
  state.a = a
  state.b = b
  state.c = 2
}
change()

Console output:

text
a were changed
b were changed
c were changed
run reaction ๐Ÿ‘Œ

TIP

Nothing break if you wonโ€™t control batching, but knowledge how it works, can help you, for example, to reduce the number of unnecessary renders in React to zero.

kroman@observable.ru