Skip to content

Commit dac3ca4

Browse files
committed
Merge branch 'gh-pages' of https://github.com/LivelyKernel/lively4-core into gh-pages
2 parents 0143571 + 6c50239 commit dac3ca4

File tree

3 files changed

+317
-0
lines changed

3 files changed

+317
-0
lines changed

src/client/reactive/active-expression/active-expression.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,20 @@ export class BaseActiveExpression {
427427
return this;
428428
}
429429

430+
gotDisposed() {
431+
if (this._disposedPromise) {
432+
return this._disposedPromise;
433+
}
434+
435+
return this._disposedPromise = new Promise(resolve => {
436+
if (this.isDisposed()) {
437+
resolve();
438+
} else {
439+
this.on('dispose', resolve);
440+
}
441+
});
442+
}
443+
430444
/*MD ## Reflection Information MD*/
431445
meta(annotation) {
432446
if(annotation) {
@@ -457,6 +471,38 @@ export class BaseActiveExpression {
457471
if(value !== undefined)this.meta({isMeta : value});
458472
else return this.meta().has('isMeta') && this.meta().get('isMeta');
459473
}
474+
475+
/*MD ## Iterators and Utility Methods MD*/
476+
nextValue() {
477+
return new Promise((resolve, reject) => {
478+
const callback = value => {
479+
this.offChange(callback);
480+
resolve(value);
481+
};
482+
this.onChange(callback);
483+
});
484+
}
485+
486+
then(...args) {
487+
return this.nextValue().then(...args);
488+
}
489+
490+
values() {
491+
const me = this;
492+
return {
493+
[Symbol.asyncIterator]() {
494+
return {
495+
next() {
496+
console.log("NEXT", me.getCurrentValue())
497+
return Promise.race([
498+
me.nextValue().then(v => ({ value: v, done: false })),
499+
me.gotDisposed().then(() => ({ done: true }))
500+
]);
501+
}
502+
};
503+
}
504+
};
505+
}
460506
}
461507

462508
export function aexpr(func, ...args) {
Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
"enable aexpr";
2+
3+
import chai, {expect} from 'src/external/chai.js';
4+
import sinon from 'src/external/sinon-3.2.1.js';
5+
import sinonChai from 'src/external/sinon-chai.js';
6+
chai.use(sinonChai);
7+
8+
describe('Iterators and Utility Methods for Active Expressions', () => {
9+
10+
describe('.gotDisposed', () => {
11+
12+
it("aexprs define .gotDisposed", () => {
13+
expect(aexpr(() => {})).to.respondTo('gotDisposed');
14+
});
15+
16+
it("caches its Promise", () => {
17+
const ae = aexpr(() => {});
18+
expect(ae.gotDisposed()).to.equal(ae.gotDisposed());
19+
});
20+
21+
it("Promise resolved if already disposed", async () => {
22+
const ae = aexpr(() => {});
23+
ae.dispose();
24+
const prom = ae.gotDisposed();
25+
26+
// promise resolved
27+
await prom;
28+
});
29+
30+
it("Promise resolves later when ae gets disposed", async () => {
31+
const ae = aexpr(() => {});
32+
const prom = ae.gotDisposed();
33+
34+
lively.sleep(30).then(() => ae.dispose());
35+
36+
await lively.sleep(15);
37+
expect(ae.isDisposed()).to.be.false;
38+
39+
// promise resolved
40+
await prom;
41+
});
42+
43+
});
44+
45+
describe('.values', () => {
46+
47+
it("aexprs define .values", () => {
48+
expect(aexpr(() => {})).to.respondTo('values');
49+
});
50+
51+
it(".values", async () => {
52+
let val = 0,
53+
spy = sinon.spy();
54+
55+
const ae = aexpr(() => val);
56+
57+
(async () => {
58+
59+
console.log("START ASYNC");
60+
61+
await lively.sleep(10);
62+
console.log("SET VAL", ae.getCurrentValue());
63+
val++;
64+
65+
await lively.sleep(10);
66+
console.log("SET VAL", ae.getCurrentValue());
67+
val++;
68+
69+
await lively.sleep(10);
70+
console.log("SET VAL", ae.getCurrentValue());
71+
val++;
72+
73+
await lively.sleep(10);
74+
console.log("DISPOSE", ae.getCurrentValue());
75+
ae.dispose();
76+
77+
await lively.sleep(10);
78+
console.log("SET VAL", ae.getCurrentValue());
79+
val++;
80+
})();
81+
82+
let j = 0;
83+
console.log("BEFORE LOOP", val, j);
84+
for await (let v of ae.values()) {
85+
j++;
86+
console.log("IN LOOP", val, v, j);
87+
expect(v).to.equal(j);
88+
}
89+
console.log("AFTER LOOP", val, j);
90+
expect(j).to.equal(3);
91+
});
92+
93+
// #TODO: next: introduce a queue of changed-expressions-results
94+
xit(".values can deal with multiple changes in a synchronous execution", async () => {
95+
let val = 0,
96+
spy = sinon.spy();
97+
98+
const ae = aexpr(() => val);
99+
100+
(async () => {
101+
102+
console.log("START ASYNC");
103+
104+
await lively.sleep(10);
105+
console.log("SET VAL", ae.getCurrentValue());
106+
val++;
107+
108+
await lively.sleep(10);
109+
console.log("SET VAL", ae.getCurrentValue());
110+
val++;
111+
112+
await lively.sleep(10);
113+
console.log("SET VAL", ae.getCurrentValue());
114+
val++;
115+
116+
await lively.sleep(10);
117+
console.log("DISPOSE", ae.getCurrentValue());
118+
ae.dispose();
119+
120+
await lively.sleep(10);
121+
console.log("SET VAL", ae.getCurrentValue());
122+
val++;
123+
})();
124+
125+
let j = 0;
126+
console.log("BEFORE LOOP", val, j);
127+
for await (let v of ae.values()) {
128+
j++;
129+
console.log("IN LOOP", val, v, j);
130+
expect(v).to.equal(j);
131+
}
132+
console.log("AFTER LOOP", val, j);
133+
expect(j).to.equal(3);
134+
});
135+
136+
describe('understanding generators', () => {
137+
138+
it("plain generators", () => {
139+
function* gen() {
140+
yield 1;
141+
yield 2;
142+
yield 3;
143+
}
144+
145+
const iter = gen()
146+
147+
expect(iter.next().value).to.equal(1);
148+
expect(iter.next().value).to.equal(2);
149+
expect(iter.next().value).to.equal(3);
150+
expect(iter.next().done).to.equal(true);
151+
});
152+
153+
it("generators and for loops", () => {
154+
function* gen() {
155+
yield 1;
156+
yield 2;
157+
yield 3;
158+
}
159+
160+
let j = 0;
161+
for (let i of gen()) {
162+
j++;
163+
expect(i).to.equal(j);
164+
}
165+
expect(j).to.equal(3);
166+
});
167+
168+
it("async generators and for await loops", async () => {
169+
async function* gen() {
170+
lively.sleep(1);
171+
yield 1;
172+
lively.sleep(1);
173+
yield Promise.resolve(2);
174+
lively.sleep(1);
175+
yield 3;
176+
}
177+
178+
let j = 0;
179+
for await (let i of gen()) {
180+
j++;
181+
expect(i).to.equal(j);
182+
}
183+
expect(j).to.equal(3);
184+
});
185+
186+
});
187+
188+
});
189+
190+
describe('nextValue', () => {
191+
192+
it("aexprs define nextValue", () => {
193+
expect(aexpr(() => {})).to.respondTo('nextValue');
194+
});
195+
196+
it("nextValue returns a Promise", () => {
197+
expect(aexpr(() => {}).nextValue()).to.be.an.instanceof(Promise);
198+
});
199+
200+
it("nextValue resolves on first change of monitored expression", async () => {
201+
let obj = {a: 1},
202+
spy = sinon.spy();
203+
204+
let axp = aexpr(() => obj.a);
205+
206+
async function waitingForChange() {
207+
const v = await axp.nextValue();
208+
spy(v);
209+
}
210+
211+
waitingForChange();
212+
213+
expect(spy).not.to.be.called;
214+
215+
obj.a = 42;
216+
// await is resolved as a separate micro task
217+
expect(spy).not.to.be.called;
218+
219+
await lively.sleep(10);
220+
221+
expect(spy).to.be.calledOnce;
222+
expect(spy).to.be.calledWith(42);
223+
spy.reset();
224+
225+
obj.a = 43;
226+
227+
await lively.sleep(10);
228+
expect(spy).not.to.be.called;
229+
});
230+
231+
describe('thenable AExprs', () => {
232+
233+
it("Active Expressions are thenables", async () => {
234+
let obj = {a: 1},
235+
spy = sinon.spy();
236+
237+
let axp = aexpr(() => obj.a);
238+
239+
async function waitingForChange() {
240+
const v = await axp;
241+
spy(v);
242+
}
243+
244+
waitingForChange();
245+
246+
expect(spy).not.to.be.called;
247+
248+
// the following await is crutial, as the `.then` on axp is executed
249+
// in waitingForChange in its own micro task only AFTER this
250+
// synchronous part is over!!!
251+
await lively.sleep(10);
252+
253+
obj.a = 17;
254+
// await is resolved as a separate micro task
255+
expect(spy).not.to.be.called;
256+
257+
await lively.sleep(10);
258+
259+
expect(spy).to.be.calledOnce;
260+
expect(spy).to.be.calledWith(17);
261+
spy.reset();
262+
263+
obj.a = 43;
264+
expect(spy).not.to.be.called;
265+
});
266+
267+
});
268+
269+
});
270+
});

src/components/tools/lively-editor.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,7 @@ export default class Editor extends Morph {
967967
}
968968

969969
async enableAnnotations() {
970+
debugger
970971
await this.loadAnnotations(this.getText(), this.lastVersion)
971972
lively.removeEventListener("annotations", this)
972973
lively.addEventListener("annotations", this, "loaded-file", async evt => {

0 commit comments

Comments
 (0)