
いくつかの情報源によると、紀元前4世紀に戻って、アリストテレスは1つの簡単な質問をしました-以前に何が起こったのですか?鶏が先か卵が先か?彼自身は最終的に両方が同時に現れたという結論に達しました-これはひねりです!そうではありませんか?
, , - . , , , — , , — , (, ..).
. . . , , .
. — , . — . ?
What's up guys?
- - .
npm i whatsup
— . . api. .
, react
+ mobx
, , 5kb gzip
.
, — , , . . — , , , , .
Cause & Conse
. . computed
observable
, , , , .
. , ( , — ).
const name = conse('John')
// - What`s up name?
whatsUp(name, (v) => console.log(v))
// :
//> "John"
name.set('Barry')
//> "Barry"
, ? conse
, whatsUp
— "" . .set(...)
— — .
Conse
Cause
. , yield*
— "" , , , yield*
return
( yield
, )
const name = conse('John')
const user = cause(function* () {
return {
name: yield* name,
// ^^^^^^ name
//
}
})
// - What`s up user? :)
whatsUp(user, (v) => console.log(v))
// :
//> {name: "John"}
name.set('Barry')
//> {name: "Barry"}
yield* name
user
name
, , — name
— user
— — .
?
, - "". . , user
revision
, .
— revision
, user
.
const name = conse('John')
let revision = 0
const user = cause(function* () {
return {
name: yield* name,
revision: revision++,
}
})
whatsUp(user, (v) => console.log(v))
//> {name: "John", revision: 0}
name.set('Barry')
//> {name: "Barry", revision: 1}
- , — revision
. — , ( ) yield
return
, , .
const name = conse('John')
const user = cause(function* () {
let revision = 0
while (true) {
yield {
name: yield* name,
revision: revision++,
}
}
})
whatsUp(user, (v) => console.log(v))
//> {name: "John", revision: 0}
name.set('Barry')
//> {name: "Barry", revision: 1}
? , , . revision
, , . revision
, — .
cause
conse
— . , .
import { Cause, Conse, whatsUp } from 'whatsup'
type UserData = { name: string }
class Name extends Conse<string> {}
class User extends Cause<UserData> {
readonly name: Name
constructor(name: string) {
super()
this.name = new Name(name)
}
*whatsUp() {
while (true) {
yield {
name: yield* this.name,
}
}
}
}
const user = new User('John')
whatsUp(user, (v) => console.log(v))
//> {name: "John"}
user.name.set('Barry')
//> {name: "Barry"}
whatsUp
, .
whatsUp
. , update
— .
. , , . , try {} finally {}
.
-, 1 , setTimeout
, , clearTimeout
.
const timer = cause(function* (ctx: Context) {
let timeoutId: number
let i = 0
try {
while (true) {
timeoutId = setTimeout(() => ctx.update(), 1000)
// 1
yield i++
//
//
}
} finally {
clearTimeout(timeoutId)
//
console.log('Timer disposed')
}
})
const dispose = whatsUp(timer, (v) => console.log(v))
//> 0
//> 1
//> 2
dispose()
//> 'Timer disposed'
—
, . .
const increment = mutator((i = -1) => i + 1)
const timer = cause(function* (ctx: Context) {
// ...
while (true) {
// ...
//
yield increment
}
// ...
})
— , . , . , undefined
, i
-1
, 0
. .. increment
i
.
. , , ===
. — . , , , . - , , , .
class EqualArr<T> extends Mutator<T[]> {
constructor(readonly next: T[]) {}
mutate(prev?: T[]) {
const { next } = this
if (
prev &&
prev.length === next.length &&
prev.every((item, i) => item === next[i])
) {
/*
, ,
, ,
*/
return prev
}
return next
}
}
const some = cause(function* () {
while (true) {
yield new EqualArr([
/*...*/
])
}
})
.. , shallowEqual
, , , . , .
cause
conse
mutator
— . , Mutator
, mutate
.
— dom-. — body
, .
class Div extends Mutator<HTMLDivElement> {
constructor(readonly text: string) {
super()
}
mutate(node = document.createElement('div')) {
node.textContent = this.text
return node
}
}
const name = conse('John')
const nameElement = cause(function* () {
while (true) {
yield new Div(yield* name)
}
})
whatsUp(nameElement, (div) => document.body.append(div))
/*
<body>
<div>John</div>
</body>
*/
name.set('Barry')
/*
<body>
<div>Barry</div>
</body>
*/
— WhatsUp — , observable
, computed
, reaction
. action
, . , , .
, :) , . , , , . parent-child
— , . , ! , , .
import { Fractal, Conse, Event, Context } from 'whatsup'
import { render } from '@whatsup/jsx'
class Theme extends Conse<string> {}
class ChangeThemeEvent extends Event {
constructor(readonly name: string) {
super()
}
}
class App extends Fractal<JSX.Element> {
readonly theme = new Theme('light');
readonly settings = new Settings()
*whatsUp(ctx: Context) {
// this.theme
// .. " "
ctx.share(this.theme)
// ChangeThemeEvent,
//
ctx.on(ChangeThemeEvent, (e) => this.theme.set(e.name))
while (true) {
yield (<div>{yield* this.settings}</div>)
}
}
}
class Settings extends Fractal<JSX.Element> {
*whatsUp(ctx: Context) {
// Theme, -
const theme = ctx.get(Theme)
// , ctx.dispath
const change = (name: string) =>
ctx.dispath(new ChangeThemeEvent(name))
while (true) {
yield (
<div>
<h1>Current</h1>
<span>{yield* theme}</span>
<h1>Choose</h1>
<button onClick={() => change('light')}>light</button>
<button onClick={() => change('dark')}>dark</button>
</div>
)
}
}
}
const app = new App()
render(app)
ctx.share
, - , ctx.get
.
ctx.on
, ctx.dispatch
. ctx.off
, , .
, jsx- babel- jsx-. ? — . , dom-, html-. dom ( react, ) . , . — , , dom Settings
( yield* theme
— ).
, <body>
. render
, , — .
, , .
. , , try {} catch {}
. , , .
import { conse, Fractal } from 'whatsup'
import { render } from '@whatsup/jsx'
class CounterMoreThan10Error extends Error {}
class App extends Fractal<JSX.Element> {
*whatsUp() {
const clicker = new Clicker()
const reset = () => clicker.reset()
while (true) {
try {
yield (<div>{yield* clicker}</div>)
} catch (e) {
// , "" - ,
//
// -
if (e instanceof CounterMoreThan10Error) {
yield (
<div>
<div>Counter more than 10, need reset</div>
<button onClick={reset}>Reset</button>
</div>
)
} else {
throw e
}
}
}
}
}
class Clicker extends Fractal<JSX.Element> {
readonly count = conse(0)
reset() {
this.count.set(0)
}
increment() {
const value = this.count.get() + 1
this.count.set(value)
}
*whatsUp() {
while (true) {
const count = yield* this.count
if (count > 10) {
throw new CounterMoreThan10Error()
}
yield (
<div>
<div>Count: {count}</div>
<button onClick={() => this.increment()}>increment</button>
</div>
)
}
}
}
const app = new App()
render(app)
, , , :
yield*
—yield
—return
—throw
—
— , whatsup
js-framework-benchmark. - , — , : , , , , , . . , whatsup
, inferno
, preact
, vue
, react
angular

, "" , . , , " " — .
-
3 kb gzip. — whatsup
. , 5-.
Glitch free
JustDont :
. , , , . , , 20. maximum call stack size exceeded.
. mobx whatsup ( ). : "", . a
, b
, c
, d
. a2 = b1
, b2 = a1-c1
, c2 = b1+d1
, d2 = c1
. "" . , "".
— Chrome 88.0.4324.104 (64-) mobx
1653 , Maximum call stack size exceeded
. — .
Whatsup
5, 10 100 000 — out of memory
. , . layersCount
.
, . , yield delegate(otherStream)
.
defer
, . , , .
@whatsup/route
— route
redirect
. , , react-router
. , ([0-9]+)
. , .
CLI
— André Lins. whatsup
- .
npm i -g @whatsup/cli
# then
whatsup project
WhatsUp
- react
-. @whatsup/react, .
Todos — TodoMVC
Loadable — , ctx.defer
, , , ,
Antistress — , , . , , . — , — , — . ,
Sierpinski — ,
, whatsup
— , — … - .
お時間をいただきありがとうございました。理由でお持ち帰りいただければ幸いです。私はあなたのコメント、建設的な批判を受け取り、プロジェクトの開発に協力していただければ幸いです。しかし、そこには何がありますか?githubのアスタリスクでさえすでに報酬です。
さらに、私を支えてくれたすべての人に感謝の意を表したいと思います。個人的に、電子メールで、vkで、電報で書きました。最初の記事の公開後、私はそのような反応を期待していませんでした。それは私にとって嬉しい驚きであり、プロジェクトの開発に対する追加のインセンティブでした。ありがとう!
よろしく、デニスCh。
「私の仕事のほとんどは、新しい科学分野の誕生の苦痛です」ブノワ・マンデルブロ