Skip to content

Latest commit

 

History

History
88 lines (63 loc) · 2.43 KB

sync-async.md

File metadata and controls

88 lines (63 loc) · 2.43 KB

Sync/Async changes delivery

Introduction

Beginning from version 3.2.0 it is possible to control whether the changes to be delivered in a sync or async fashion.

The opt-in mechanism works on the level of Observable, via the options passed to the static Observable.from method during the creation and affects all callbacks/observers that will be listening on the observable instance.

Asynchronous behaviour implemented via queueMicrotask mechanism, mimicing a similar native implementations like Promise, MutationObserver etc.

Which one to use? I suggest to go first with a default sync flavor and switch to the async only when it is proven to be needed, for example batch delivery of changes of Object.assign (see below).

Sync

Default behaviour is to deliver changes synchronously:

const o = Observable.from({});
Observable.observe(o, changes =>
	changes.forEach(change => console.log(change.value))
);

o.propA = 1;
console.log(2);
o.propB = 3;

//	console output:
//	1
//	2
//	3

Here is the place to mention somewhat unexpected outcome of this flavor in case of Object.assign usage:

const o = Observable.from({});
let callbacksCount = 0;
Observable.observe(o, () => callbacksCount++);

Object.assign(o, { a: 1, b: 2, c: 3 });
console.log(callbacksCount);

//	console output:
//	3

While one would expect to have a single callback with 3 changes, in fact we've got 3 callback, each with a single change. Object.assign does a full assignment cycle per property, including passing via the Proxy traps, which causes this single operation to appear as 3 separate assignements.

Async

One may opt-in an asynchronous changes delivery:

const o = Observable.from({}, { async: true });
Observable.observe(o, changes =>
	changes.forEach(change => console.log(change.value))
);

o.propA = 3;
console.log(1);
o.propB = 4;
console.log(2);

//	console output:
//	1
//	2
//	3
//	4

In this case Object.assign will behave correctly:

const o = Observable.from({});
let callbacksCount = 0;
Observable.observe(o, () => callbacksCount++);

Object.assign(o, { a: 1, b: 2, c: 3 });

queueMicrotask(() =>
	console.log(callbacksCount)
);

//	console output:
//	1

Now, due to postponing changes delivery to the end of the currently running task, all 3 changes are delivered in a single callback.

Pay attention, that because of the asynchronicity, we also need to postpone the inspection of the effect of the callback/s.