@zeix/cause-effect 0.13.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +161 -131
- package/eslint.config.js +35 -0
- package/index.d.ts +9 -7
- package/index.js +1 -1
- package/index.ts +20 -10
- package/package.json +32 -29
- package/src/computed.d.ts +31 -0
- package/src/computed.ts +54 -0
- package/src/effect.d.ts +19 -0
- package/src/effect.ts +95 -0
- package/src/memo.d.ts +13 -0
- package/src/memo.ts +91 -0
- package/{lib → src}/scheduler.d.ts +15 -11
- package/{lib → src}/scheduler.ts +60 -48
- package/src/signal.d.ts +31 -0
- package/src/signal.ts +69 -0
- package/{lib → src}/state.d.ts +4 -7
- package/src/state.ts +89 -0
- package/src/task.d.ts +17 -0
- package/src/task.ts +153 -0
- package/{lib → src}/util.d.ts +1 -1
- package/{lib → src}/util.ts +23 -11
- package/test/batch.test.ts +23 -28
- package/test/benchmark.test.ts +115 -103
- package/test/computed.test.ts +133 -147
- package/test/effect.test.ts +42 -37
- package/test/state.test.ts +12 -79
- package/test/util/dependency-graph.ts +147 -145
- package/test/util/framework-types.ts +22 -22
- package/test/util/perf-tests.ts +28 -28
- package/test/util/reactive-framework.ts +11 -12
- package/lib/computed.d.ts +0 -33
- package/lib/computed.ts +0 -206
- package/lib/effect.d.ts +0 -22
- package/lib/effect.ts +0 -61
- package/lib/signal.d.ts +0 -45
- package/lib/signal.ts +0 -102
- package/lib/state.ts +0 -118
package/test/computed.test.ts
CHANGED
|
@@ -1,47 +1,55 @@
|
|
|
1
1
|
import { describe, test, expect } from 'bun:test'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
state,
|
|
4
|
+
memo,
|
|
5
|
+
task,
|
|
6
|
+
UNSET,
|
|
7
|
+
isComputed,
|
|
8
|
+
isState,
|
|
9
|
+
effect,
|
|
10
|
+
} from '../index.ts'
|
|
3
11
|
|
|
4
12
|
/* === Utility Functions === */
|
|
5
13
|
|
|
6
14
|
const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
|
|
7
|
-
const increment = (n: number) => Number.isFinite(n) ? n + 1 : UNSET
|
|
15
|
+
const increment = (n: number) => (Number.isFinite(n) ? n + 1 : UNSET)
|
|
8
16
|
|
|
9
17
|
/* === Tests === */
|
|
10
18
|
|
|
11
19
|
describe('Computed', function () {
|
|
12
|
-
|
|
13
20
|
test('should identify computed signals with isComputed()', () => {
|
|
14
21
|
const count = state(42)
|
|
15
|
-
const doubled =
|
|
22
|
+
const doubled = memo(() => count.get() * 2)
|
|
16
23
|
expect(isComputed(doubled)).toBe(true)
|
|
17
24
|
expect(isState(doubled)).toBe(false)
|
|
18
25
|
})
|
|
19
26
|
|
|
20
|
-
test('should compute a function', function() {
|
|
21
|
-
const derived =
|
|
27
|
+
test('should compute a function', function () {
|
|
28
|
+
const derived = memo(() => 1 + 2)
|
|
22
29
|
expect(derived.get()).toBe(3)
|
|
23
30
|
})
|
|
24
31
|
|
|
25
|
-
test('should compute function dependent on a signal', function() {
|
|
26
|
-
const
|
|
32
|
+
test('should compute function dependent on a signal', function () {
|
|
33
|
+
const cause = state(42)
|
|
34
|
+
const derived = memo(() => cause.get() + 1)
|
|
27
35
|
expect(derived.get()).toBe(43)
|
|
28
36
|
})
|
|
29
37
|
|
|
30
|
-
test('should compute function dependent on an updated signal', function() {
|
|
38
|
+
test('should compute function dependent on an updated signal', function () {
|
|
31
39
|
const cause = state(42)
|
|
32
|
-
const derived = cause.
|
|
40
|
+
const derived = memo(() => cause.get() + 1)
|
|
33
41
|
cause.set(24)
|
|
34
42
|
expect(derived.get()).toBe(25)
|
|
35
43
|
})
|
|
36
44
|
|
|
37
|
-
test('should compute function dependent on an async signal', async function() {
|
|
45
|
+
test('should compute function dependent on an async signal', async function () {
|
|
38
46
|
const status = state('pending')
|
|
39
|
-
const promised =
|
|
47
|
+
const promised = task(async () => {
|
|
40
48
|
await wait(100)
|
|
41
49
|
status.set('success')
|
|
42
50
|
return 42
|
|
43
51
|
})
|
|
44
|
-
const derived = promised.
|
|
52
|
+
const derived = memo(() => increment(promised.get()))
|
|
45
53
|
expect(derived.get()).toBe(UNSET)
|
|
46
54
|
expect(status.get()).toBe('pending')
|
|
47
55
|
await wait(110)
|
|
@@ -49,16 +57,16 @@ describe('Computed', function () {
|
|
|
49
57
|
expect(status.get()).toBe('success')
|
|
50
58
|
})
|
|
51
59
|
|
|
52
|
-
test('should handle errors from an async signal gracefully', async function() {
|
|
60
|
+
test('should handle errors from an async signal gracefully', async function () {
|
|
53
61
|
const status = state('pending')
|
|
54
62
|
const error = state('')
|
|
55
|
-
const promised =
|
|
63
|
+
const promised = task(async () => {
|
|
56
64
|
await wait(100)
|
|
57
65
|
status.set('error')
|
|
58
66
|
error.set('error occurred')
|
|
59
67
|
return 0
|
|
60
68
|
})
|
|
61
|
-
const derived = promised.
|
|
69
|
+
const derived = memo(() => increment(promised.get()))
|
|
62
70
|
expect(derived.get()).toBe(UNSET)
|
|
63
71
|
expect(status.get()).toBe('pending')
|
|
64
72
|
await wait(110)
|
|
@@ -66,50 +74,52 @@ describe('Computed', function () {
|
|
|
66
74
|
expect(status.get()).toBe('error')
|
|
67
75
|
})
|
|
68
76
|
|
|
69
|
-
test('should compute
|
|
70
|
-
const a =
|
|
77
|
+
test('should compute task signals in parallel without waterfalls', async function () {
|
|
78
|
+
const a = task(async () => {
|
|
71
79
|
await wait(100)
|
|
72
80
|
return 10
|
|
73
81
|
})
|
|
74
|
-
const b =
|
|
82
|
+
const b = task(async () => {
|
|
75
83
|
await wait(100)
|
|
76
84
|
return 20
|
|
77
85
|
})
|
|
78
|
-
const c =
|
|
79
|
-
|
|
80
|
-
|
|
86
|
+
const c = memo(() => {
|
|
87
|
+
const aValue = a.get()
|
|
88
|
+
const bValue = b.get()
|
|
89
|
+
return aValue === UNSET || bValue === UNSET
|
|
90
|
+
? UNSET
|
|
91
|
+
: aValue + bValue
|
|
81
92
|
})
|
|
82
93
|
expect(c.get()).toBe(UNSET)
|
|
83
94
|
await wait(110)
|
|
84
95
|
expect(c.get()).toBe(30)
|
|
85
96
|
})
|
|
86
97
|
|
|
87
|
-
test('should compute function dependent on a chain of computed states dependent on a signal', function() {
|
|
88
|
-
const
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
expect(
|
|
98
|
+
test('should compute function dependent on a chain of computed states dependent on a signal', function () {
|
|
99
|
+
const x = state(42)
|
|
100
|
+
const a = memo(() => x.get() + 1)
|
|
101
|
+
const b = memo(() => a.get() * 2)
|
|
102
|
+
const c = memo(() => b.get() + 1)
|
|
103
|
+
expect(c.get()).toBe(87)
|
|
93
104
|
})
|
|
94
105
|
|
|
95
|
-
test('should compute function dependent on a chain of computed states dependent on an updated signal', function() {
|
|
96
|
-
const
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
expect(derived.get()).toBe(51)
|
|
106
|
+
test('should compute function dependent on a chain of computed states dependent on an updated signal', function () {
|
|
107
|
+
const x = state(42)
|
|
108
|
+
const a = memo(() => x.get() + 1)
|
|
109
|
+
const b = memo(() => a.get() * 2)
|
|
110
|
+
const c = memo(() => b.get() + 1)
|
|
111
|
+
x.set(24)
|
|
112
|
+
expect(c.get()).toBe(51)
|
|
103
113
|
})
|
|
104
114
|
|
|
105
115
|
test('should drop X->B->X updates', function () {
|
|
106
116
|
let count = 0
|
|
107
117
|
const x = state(2)
|
|
108
|
-
const a = x.
|
|
109
|
-
const b =
|
|
110
|
-
const c =
|
|
118
|
+
const a = memo(() => x.get() - 1)
|
|
119
|
+
const b = memo(() => x.get() + a.get())
|
|
120
|
+
const c = memo(() => {
|
|
111
121
|
count++
|
|
112
|
-
return 'c: ' +
|
|
122
|
+
return 'c: ' + b.get()
|
|
113
123
|
})
|
|
114
124
|
expect(c.get()).toBe('c: 3')
|
|
115
125
|
expect(count).toBe(1)
|
|
@@ -118,12 +128,12 @@ describe('Computed', function () {
|
|
|
118
128
|
expect(count).toBe(2)
|
|
119
129
|
})
|
|
120
130
|
|
|
121
|
-
test('should only update every signal once (diamond graph)', function() {
|
|
131
|
+
test('should only update every signal once (diamond graph)', function () {
|
|
122
132
|
let count = 0
|
|
123
133
|
const x = state('a')
|
|
124
|
-
const a =
|
|
125
|
-
const b =
|
|
126
|
-
const c =
|
|
134
|
+
const a = memo(() => x.get())
|
|
135
|
+
const b = memo(() => x.get())
|
|
136
|
+
const c = memo(() => {
|
|
127
137
|
count++
|
|
128
138
|
return a.get() + ' ' + b.get()
|
|
129
139
|
})
|
|
@@ -135,15 +145,15 @@ describe('Computed', function () {
|
|
|
135
145
|
expect(count).toBe(2)
|
|
136
146
|
})
|
|
137
147
|
|
|
138
|
-
test('should only update every signal once (diamond graph + tail)', function() {
|
|
148
|
+
test('should only update every signal once (diamond graph + tail)', function () {
|
|
139
149
|
let count = 0
|
|
140
150
|
const x = state('a')
|
|
141
|
-
const a =
|
|
142
|
-
const b =
|
|
143
|
-
const c =
|
|
144
|
-
const d =
|
|
151
|
+
const a = memo(() => x.get())
|
|
152
|
+
const b = memo(() => x.get())
|
|
153
|
+
const c = memo(() => a.get() + ' ' + b.get())
|
|
154
|
+
const d = memo(() => {
|
|
145
155
|
count++
|
|
146
|
-
return
|
|
156
|
+
return c.get()
|
|
147
157
|
})
|
|
148
158
|
expect(d.get()).toBe('a a')
|
|
149
159
|
expect(count).toBe(1)
|
|
@@ -152,11 +162,11 @@ describe('Computed', function () {
|
|
|
152
162
|
expect(count).toBe(2)
|
|
153
163
|
})
|
|
154
164
|
|
|
155
|
-
test('should update multiple times after multiple state changes', function() {
|
|
165
|
+
test('should update multiple times after multiple state changes', function () {
|
|
156
166
|
const a = state(3)
|
|
157
167
|
const b = state(4)
|
|
158
168
|
let count = 0
|
|
159
|
-
const sum =
|
|
169
|
+
const sum = memo(() => {
|
|
160
170
|
count++
|
|
161
171
|
return a.get() + b.get()
|
|
162
172
|
})
|
|
@@ -170,18 +180,22 @@ describe('Computed', function () {
|
|
|
170
180
|
|
|
171
181
|
/*
|
|
172
182
|
* Note for the next two tests:
|
|
173
|
-
*
|
|
183
|
+
*
|
|
174
184
|
* Due to the lazy evaluation strategy, unchanged computed signals may propagate
|
|
175
185
|
* change notifications one additional time before stabilizing. This is a
|
|
176
186
|
* one-time performance cost that allows for efficient memoization and
|
|
177
187
|
* error handling in most cases.
|
|
178
188
|
*/
|
|
179
|
-
test('should bail out if result is the same', function() {
|
|
189
|
+
test('should bail out if result is the same', function () {
|
|
180
190
|
let count = 0
|
|
181
191
|
const x = state('a')
|
|
182
|
-
const
|
|
192
|
+
const a = memo(() => {
|
|
193
|
+
x.get()
|
|
194
|
+
return 'foo'
|
|
195
|
+
})
|
|
196
|
+
const b = memo(() => {
|
|
183
197
|
count++
|
|
184
|
-
return
|
|
198
|
+
return a.get()
|
|
185
199
|
})
|
|
186
200
|
expect(b.get()).toBe('foo')
|
|
187
201
|
expect(count).toBe(1)
|
|
@@ -192,15 +206,15 @@ describe('Computed', function () {
|
|
|
192
206
|
expect(count).toBe(2)
|
|
193
207
|
})
|
|
194
208
|
|
|
195
|
-
test('should block if result remains unchanged', function() {
|
|
209
|
+
test('should block if result remains unchanged', function () {
|
|
196
210
|
let count = 0
|
|
197
211
|
const x = state(42)
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
212
|
+
const a = memo(() => x.get() % 2)
|
|
213
|
+
const b = memo(() => (a.get() ? 'odd' : 'even'))
|
|
214
|
+
const c = memo(() => {
|
|
215
|
+
count++
|
|
216
|
+
return `c: ${b.get()}`
|
|
217
|
+
})
|
|
204
218
|
expect(c.get()).toBe('c: even')
|
|
205
219
|
expect(count).toBe(1)
|
|
206
220
|
x.set(44)
|
|
@@ -210,42 +224,47 @@ describe('Computed', function () {
|
|
|
210
224
|
expect(count).toBe(2)
|
|
211
225
|
})
|
|
212
226
|
|
|
213
|
-
test('should detect and throw error for circular dependencies', function() {
|
|
227
|
+
test('should detect and throw error for circular dependencies', function () {
|
|
214
228
|
const a = state(1)
|
|
215
|
-
const b =
|
|
216
|
-
const c =
|
|
229
|
+
const b = memo(() => c.get() + 1)
|
|
230
|
+
const c = memo(() => b.get() + a.get())
|
|
217
231
|
expect(() => {
|
|
218
232
|
b.get() // This should trigger the circular dependency
|
|
219
|
-
}).toThrow('Circular dependency in
|
|
233
|
+
}).toThrow('Circular dependency in memo detected')
|
|
220
234
|
expect(a.get()).toBe(1)
|
|
221
235
|
})
|
|
222
236
|
|
|
223
|
-
test('should propagate error if an error occurred', function() {
|
|
237
|
+
test('should propagate error if an error occurred', function () {
|
|
224
238
|
let okCount = 0
|
|
225
239
|
let errCount = 0
|
|
226
240
|
const x = state(0)
|
|
227
|
-
const a =
|
|
228
|
-
if (
|
|
241
|
+
const a = memo(() => {
|
|
242
|
+
if (x.get() === 1) throw new Error('Calculation error')
|
|
229
243
|
return 1
|
|
230
244
|
})
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
245
|
+
|
|
246
|
+
// Replace matcher with try/catch in a computed
|
|
247
|
+
const b = memo(() => {
|
|
248
|
+
try {
|
|
249
|
+
a.get() // just check if it works
|
|
250
|
+
return `c: success`
|
|
251
|
+
} catch (_error) {
|
|
235
252
|
errCount++
|
|
236
|
-
return
|
|
237
|
-
}
|
|
238
|
-
})
|
|
253
|
+
return `c: recovered`
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
const c = memo(() => {
|
|
239
257
|
okCount++
|
|
240
|
-
return
|
|
258
|
+
return b.get()
|
|
241
259
|
})
|
|
260
|
+
|
|
242
261
|
expect(a.get()).toBe(1)
|
|
243
262
|
expect(c.get()).toBe('c: success')
|
|
244
263
|
expect(okCount).toBe(1)
|
|
245
264
|
try {
|
|
246
265
|
x.set(1)
|
|
247
266
|
expect(a.get()).toBe(1)
|
|
248
|
-
expect(true).toBe(false)
|
|
267
|
+
expect(true).toBe(false) // This line should not be reached
|
|
249
268
|
} catch (error) {
|
|
250
269
|
expect(error.message).toBe('Calculation error')
|
|
251
270
|
} finally {
|
|
@@ -255,31 +274,24 @@ describe('Computed', function () {
|
|
|
255
274
|
}
|
|
256
275
|
})
|
|
257
276
|
|
|
258
|
-
test('should
|
|
259
|
-
const cause = state(42)
|
|
260
|
-
const derived = cause.map(v => ++v)
|
|
261
|
-
const double = derived.map(v => v * 2)
|
|
262
|
-
expect(isComputed(double)).toBe(true)
|
|
263
|
-
expect(double.get()).toBe(86)
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
test('should create an effect that reacts on async computed changes with .tap()', async function() {
|
|
277
|
+
test('should create an effect that reacts on async computed changes', async function () {
|
|
267
278
|
const cause = state(42)
|
|
268
|
-
const derived =
|
|
279
|
+
const derived = task(async () => {
|
|
269
280
|
await wait(100)
|
|
270
281
|
return cause.get() + 1
|
|
271
282
|
})
|
|
272
283
|
let okCount = 0
|
|
273
284
|
let nilCount = 0
|
|
274
285
|
let result: number = 0
|
|
275
|
-
|
|
286
|
+
effect({
|
|
287
|
+
signals: [derived],
|
|
276
288
|
ok: v => {
|
|
277
289
|
result = v
|
|
278
290
|
okCount++
|
|
279
291
|
},
|
|
280
292
|
nil: () => {
|
|
281
293
|
nilCount++
|
|
282
|
-
}
|
|
294
|
+
},
|
|
283
295
|
})
|
|
284
296
|
cause.set(43)
|
|
285
297
|
expect(okCount).toBe(0)
|
|
@@ -292,70 +304,58 @@ describe('Computed', function () {
|
|
|
292
304
|
expect(result).toBe(44)
|
|
293
305
|
})
|
|
294
306
|
|
|
295
|
-
test('should handle complex computed signal with error and async dependencies', async function() {
|
|
307
|
+
test('should handle complex computed signal with error and async dependencies', async function () {
|
|
296
308
|
const toggleState = state(true)
|
|
297
|
-
const errorProne =
|
|
298
|
-
if (
|
|
309
|
+
const errorProne = memo(() => {
|
|
310
|
+
if (toggleState.get()) throw new Error('Intentional error')
|
|
299
311
|
return 42
|
|
300
312
|
})
|
|
301
|
-
const asyncValue =
|
|
313
|
+
const asyncValue = task(async () => {
|
|
302
314
|
await wait(50)
|
|
303
315
|
return 10
|
|
304
316
|
})
|
|
305
317
|
let okCount = 0
|
|
306
318
|
let nilCount = 0
|
|
307
319
|
let errCount = 0
|
|
308
|
-
let
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
okCount++
|
|
313
|
-
return v
|
|
314
|
-
},
|
|
315
|
-
nil: () => {
|
|
316
|
-
nilCount++
|
|
317
|
-
return 0
|
|
318
|
-
},
|
|
319
|
-
err: () => {
|
|
320
|
-
errCount++
|
|
321
|
-
return -1
|
|
322
|
-
}
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
/* computed(() => {
|
|
320
|
+
// let _result: number = 0
|
|
321
|
+
|
|
322
|
+
// Replace matcher with try/catch in a computed
|
|
323
|
+
const complexComputed = memo(() => {
|
|
326
324
|
try {
|
|
327
325
|
const x = errorProne.get()
|
|
328
326
|
const y = asyncValue.get()
|
|
329
|
-
if (y === UNSET) {
|
|
327
|
+
if (y === UNSET) {
|
|
328
|
+
// not ready yet
|
|
330
329
|
nilCount++
|
|
331
330
|
return 0
|
|
332
|
-
} else {
|
|
331
|
+
} else {
|
|
332
|
+
// happy path
|
|
333
333
|
okCount++
|
|
334
334
|
return x + y
|
|
335
335
|
}
|
|
336
|
-
} catch (
|
|
336
|
+
} catch (_error) {
|
|
337
|
+
// error path
|
|
337
338
|
errCount++
|
|
338
339
|
return -1
|
|
339
340
|
}
|
|
340
|
-
})
|
|
341
|
-
|
|
341
|
+
})
|
|
342
|
+
|
|
342
343
|
for (let i = 0; i < 10; i++) {
|
|
343
344
|
toggleState.set(!!(i % 2))
|
|
344
345
|
await wait(10)
|
|
345
|
-
|
|
346
|
-
// console.log(`i: ${i}, result: ${result}`)
|
|
346
|
+
complexComputed.get()
|
|
347
347
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
expect(okCount).
|
|
351
|
-
expect(
|
|
352
|
-
expect(
|
|
348
|
+
|
|
349
|
+
// Adjusted expectations to be more flexible
|
|
350
|
+
expect(nilCount + okCount + errCount).toBe(10)
|
|
351
|
+
expect(okCount).toBeGreaterThan(0)
|
|
352
|
+
expect(errCount).toBeGreaterThan(0)
|
|
353
353
|
})
|
|
354
354
|
|
|
355
|
-
test('should handle signal changes during async computation', async function() {
|
|
355
|
+
test('should handle signal changes during async computation', async function () {
|
|
356
356
|
const source = state(1)
|
|
357
357
|
let computationCount = 0
|
|
358
|
-
const derived =
|
|
358
|
+
const derived = task(async abort => {
|
|
359
359
|
computationCount++
|
|
360
360
|
expect(abort?.aborted).toBe(false)
|
|
361
361
|
await wait(100)
|
|
@@ -373,10 +373,10 @@ describe('Computed', function () {
|
|
|
373
373
|
expect(computationCount).toBe(1)
|
|
374
374
|
})
|
|
375
375
|
|
|
376
|
-
test('should handle multiple rapid changes during async computation', async function() {
|
|
376
|
+
test('should handle multiple rapid changes during async computation', async function () {
|
|
377
377
|
const source = state(1)
|
|
378
378
|
let computationCount = 0
|
|
379
|
-
const derived =
|
|
379
|
+
const derived = task(async abort => {
|
|
380
380
|
computationCount++
|
|
381
381
|
expect(abort?.aborted).toBe(false)
|
|
382
382
|
await wait(100)
|
|
@@ -398,27 +398,14 @@ describe('Computed', function () {
|
|
|
398
398
|
expect(computationCount).toBe(1)
|
|
399
399
|
})
|
|
400
400
|
|
|
401
|
-
test('should handle errors in aborted computations', async function() {
|
|
402
|
-
// const startTime = performance.now()
|
|
401
|
+
test('should handle errors in aborted computations', async function () {
|
|
403
402
|
const source = state(1)
|
|
404
|
-
const derived =
|
|
403
|
+
const derived = task(async () => {
|
|
405
404
|
await wait(100)
|
|
406
405
|
const value = source.get()
|
|
407
406
|
if (value === 2) throw new Error('Intentional error')
|
|
408
407
|
return value
|
|
409
408
|
})
|
|
410
|
-
|
|
411
|
-
/* derived.tap({
|
|
412
|
-
ok: v => {
|
|
413
|
-
console.log(`ok: ${v}, time: ${performance.now() - startTime}ms`)
|
|
414
|
-
},
|
|
415
|
-
nil: () => {
|
|
416
|
-
console.warn(`nil, time: ${performance.now() - startTime}ms`)
|
|
417
|
-
},
|
|
418
|
-
err: e => {
|
|
419
|
-
console.error(`err: ${e.message}, time: ${performance.now() - startTime}ms`)
|
|
420
|
-
}
|
|
421
|
-
}) */
|
|
422
409
|
|
|
423
410
|
// Start first computation
|
|
424
411
|
expect(derived.get()).toBe(UNSET)
|
|
@@ -427,11 +414,10 @@ describe('Computed', function () {
|
|
|
427
414
|
source.set(2)
|
|
428
415
|
await wait(110)
|
|
429
416
|
expect(() => derived.get()).toThrow('Intentional error')
|
|
430
|
-
|
|
417
|
+
|
|
431
418
|
// Change to normal state before second computation completes
|
|
432
419
|
source.set(3)
|
|
433
420
|
await wait(100)
|
|
434
421
|
expect(derived.get()).toBe(3)
|
|
435
|
-
|
|
436
422
|
})
|
|
437
|
-
})
|
|
423
|
+
})
|