Skip to content

Commit d8d9c58

Browse files
committed
Merge branch 'await-helper' into gh-956
2 parents f65d56b + 8d772b1 commit d8d9c58

File tree

7 files changed

+141
-86
lines changed

7 files changed

+141
-86
lines changed

src/compile/dom/Block.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export default class Block {
3939
destroy: CodeBuilder;
4040
};
4141

42+
maintainContext: boolean;
4243
hasIntroMethod: boolean;
4344
hasOutroMethod: boolean;
4445
outros: number;

src/compile/nodes/AwaitBlock.ts

Lines changed: 35 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -77,102 +77,54 @@ export default class AwaitBlock extends Node {
7777

7878
const { snippet } = this.expression;
7979

80+
const info = block.getUniqueName(`info`);
8081
const promise = block.getUniqueName(`promise`);
81-
const resolved = block.getUniqueName(`resolved`);
82-
const await_block = block.getUniqueName(`await_block`);
83-
const await_block_type = block.getUniqueName(`await_block_type`);
84-
const token = block.getUniqueName(`token`);
85-
const await_token = block.getUniqueName(`await_token`);
86-
const handle_promise = block.getUniqueName(`handle_promise`);
87-
const replace_await_block = block.getUniqueName(`replace_await_block`);
88-
const old_block = block.getUniqueName(`old_block`);
89-
const value = block.getUniqueName(`value`);
90-
const error = block.getUniqueName(`error`);
91-
const create_pending_block = this.pending.block.name;
92-
const create_then_block = this.then.block.name;
93-
const create_catch_block = this.catch.block.name;
94-
95-
block.addVariable(await_block);
96-
block.addVariable(await_block_type);
97-
block.addVariable(await_token);
82+
9883
block.addVariable(promise);
99-
block.addVariable(resolved);
10084

10185
block.maintainContext = true;
10286

87+
const infoProps = [
88+
block.alias('component') === 'component' ? 'component' : `component: #component`,
89+
'ctx',
90+
'current: null',
91+
this.pending.block.name && `pending: ${this.pending.block.name}`,
92+
this.then.block.name && `then: ${this.then.block.name}`,
93+
this.catch.block.name && `catch: ${this.catch.block.name}`,
94+
this.then.block.name && `value: '${this.value}'`,
95+
this.catch.block.name && `error: '${this.error}'`
96+
].filter(Boolean);
97+
98+
block.builders.init.addBlock(deindent`
99+
let ${info} = {
100+
${infoProps.join(',\n')}
101+
};
102+
`);
103+
103104
// the `#component.root.set({})` below is just a cheap way to flush
104105
// any oncreate handlers. We could have a dedicated `flush()` method
105106
// but it's probably not worth it
106107

107108
block.builders.init.addBlock(deindent`
108-
function ${replace_await_block}(${token}, type, ctx) {
109-
if (${token} !== ${await_token}) return;
110-
111-
var ${old_block} = ${await_block};
112-
${await_block} = type && (${await_block_type} = type)(#component, ctx);
113-
114-
if (${old_block}) {
115-
${old_block}.u();
116-
${old_block}.d();
117-
${await_block}.c();
118-
${await_block}.m(${updateMountNode}, ${anchor});
119-
120-
#component.root.set({});
121-
}
122-
}
123-
124-
function ${handle_promise}(${promise}) {
125-
var ${token} = ${await_token} = {};
126-
127-
if (@isPromise(${promise})) {
128-
${promise}.then(function(${value}) {
129-
${this.value ? deindent`
130-
${resolved} = { ${this.value}: ${value} };
131-
${replace_await_block}(${token}, ${create_then_block}, @assign(@assign({}, ctx), ${resolved}));
132-
` : deindent`
133-
${replace_await_block}(${token}, null, null);
134-
`}
135-
}, function (${error}) {
136-
${this.error ? deindent`
137-
${resolved} = { ${this.error}: ${error} };
138-
${replace_await_block}(${token}, ${create_catch_block}, @assign(@assign({}, ctx), ${resolved}));
139-
` : deindent`
140-
${replace_await_block}(${token}, null, null);
141-
`}
142-
});
143-
144-
// if we previously had a then/catch block, destroy it
145-
if (${await_block_type} !== ${create_pending_block}) {
146-
${replace_await_block}(${token}, ${create_pending_block}, ctx);
147-
return true;
148-
}
149-
} else {
150-
${resolved} = { ${this.value}: ${promise} };
151-
if (${await_block_type} !== ${create_then_block}) {
152-
${replace_await_block}(${token}, ${create_then_block}, @assign(@assign({}, ctx), ${resolved}));
153-
return true;
154-
}
155-
}
156-
}
157-
158-
${handle_promise}(${promise} = ${snippet});
109+
@handlePromise(${promise} = ${snippet}, ${info});
159110
`);
160111

161112
block.builders.create.addBlock(deindent`
162-
${await_block}.c();
113+
${info}.block.c();
163114
`);
164115

165116
if (parentNodes) {
166117
block.builders.claim.addBlock(deindent`
167-
${await_block}.l(${parentNodes});
118+
${info}.block.l(${parentNodes});
168119
`);
169120
}
170121

171122
const initialMountNode = parentNode || '#target';
172123
const anchorNode = parentNode ? 'null' : 'anchor';
173124

174125
block.builders.mount.addBlock(deindent`
175-
${await_block}.m(${initialMountNode}, ${anchorNode});
126+
${info}.block.m(${initialMountNode}, ${info}.anchor = ${anchorNode});
127+
${info}.mount = () => ${updateMountNode};
176128
`);
177129

178130
const conditions = [];
@@ -184,38 +136,39 @@ export default class AwaitBlock extends Node {
184136

185137
conditions.push(
186138
`${promise} !== (${promise} = ${snippet})`,
187-
`${handle_promise}(${promise}, ctx)`
139+
`@handlePromise(${promise}, ${info})`
140+
);
141+
142+
block.builders.update.addLine(
143+
`${info}.ctx = ctx;`
188144
);
189145

190146
if (this.pending.block.hasUpdateMethod) {
191147
block.builders.update.addBlock(deindent`
192148
if (${conditions.join(' && ')}) {
193149
// nothing
194150
} else {
195-
${await_block}.p(changed, @assign(@assign({}, ctx), ${resolved}));
151+
${info}.block.p(changed, @assign(@assign({}, ctx), ${info}.resolved));
196152
}
197153
`);
198154
} else {
199155
block.builders.update.addBlock(deindent`
200-
if (${conditions.join(' && ')}) {
201-
${await_block}.c();
202-
${await_block}.m(${anchor}.parentNode, ${anchor});
203-
}
156+
${conditions.join(' && ')}
204157
`);
205158
}
206159

207160
block.builders.unmount.addBlock(deindent`
208-
${await_block}.u();
161+
${info}.block.u();
209162
`);
210163

211164
block.builders.destroy.addBlock(deindent`
212-
${await_token} = null;
213-
${await_block}.d();
165+
${info}.block.d();
166+
${info} = null;
214167
`);
215168

216169
[this.pending, this.then, this.catch].forEach(status => {
217170
status.children.forEach(child => {
218-
child.build(status.block, null,'nodes');
171+
child.build(status.block, null, 'nodes');
219172
});
220173
});
221174
}

src/shared/await-block.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { assign, isPromise } from './utils.js';
2+
3+
export function handlePromise(promise, info) {
4+
var token = info.token = {};
5+
6+
function update(type, key, value) {
7+
if (info.token !== token) return;
8+
9+
info.resolved = key && { [key]: value };
10+
11+
const child_ctx = assign(assign({}, info.ctx), info.resolved);
12+
const block = type && (info.current = type)(info.component, child_ctx);
13+
14+
if (info.block) {
15+
info.block.u();
16+
info.block.d();
17+
block.c();
18+
block.m(info.mount(), info.anchor);
19+
20+
info.component.root.set({});
21+
}
22+
23+
info.block = block;
24+
}
25+
26+
if (isPromise(promise)) {
27+
promise.then(value => {
28+
update(info.then, info.value, value);
29+
}, error => {
30+
update(info.catch, info.error, error);
31+
});
32+
33+
// if we previously had a then/catch block, destroy it
34+
if (info.current !== info.pending) {
35+
update(info.pending);
36+
return true;
37+
}
38+
} else {
39+
if (info.current !== info.then) {
40+
update(info.then, info.value, promise);
41+
return true;
42+
}
43+
44+
info.resolved = { [info.value]: promise };
45+
}
46+
}

src/shared/index.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { assign } from './utils.js';
22
import { noop } from './utils.js';
3+
export * from './await-block.js';
34
export * from './dom.js';
45
export * from './keyed-each.js';
56
export * from './spread.js';
@@ -136,10 +137,6 @@ export function _unmount() {
136137
if (this._fragment) this._fragment.u();
137138
}
138139

139-
export function isPromise(value) {
140-
return value && typeof value.then === 'function';
141-
}
142-
143140
export var PENDING = {};
144141
export var SUCCESS = {};
145142
export var FAILURE = {};

src/shared/utils.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,8 @@ export function assign(tar, src) {
88
export function assignTrue(tar, src) {
99
for (var k in src) tar[k] = 1;
1010
return tar;
11+
}
12+
13+
export function isPromise(value) {
14+
return value && typeof value.then === 'function';
1115
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
let fulfil;
2+
3+
let promise = new Promise(f => {
4+
fulfil = f;
5+
});
6+
7+
export default {
8+
data: {
9+
promise
10+
},
11+
12+
html: `
13+
<p>loading...</p>
14+
`,
15+
16+
test(assert, component, target) {
17+
fulfil(42);
18+
19+
return promise
20+
.then(() => {
21+
assert.htmlEqual(target.innerHTML, `
22+
<p>loaded</p>
23+
`);
24+
25+
promise = new Promise((f, r) => {
26+
fulfil = f;
27+
});
28+
29+
component.set({
30+
promise
31+
});
32+
33+
assert.htmlEqual(target.innerHTML, `
34+
<p>loading...</p>
35+
`);
36+
37+
fulfil(43);
38+
39+
return promise.then(() => {});
40+
})
41+
.then(() => {
42+
assert.htmlEqual(target.innerHTML, `
43+
<p>loaded</p>
44+
`);
45+
});
46+
}
47+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{#await promise}
2+
<p>loading...</p>
3+
{:then value}
4+
<p>loaded</p>
5+
{:catch error}
6+
<p>errored</p>
7+
{/await}

0 commit comments

Comments
 (0)