Merge branch 'await-helper' into gh-956

pull/1420/head
Rich Harris 6 years ago
commit d8d9c58c63

@ -39,6 +39,7 @@ export default class Block {
destroy: CodeBuilder;
};
maintainContext: boolean;
hasIntroMethod: boolean;
hasOutroMethod: boolean;
outros: number;

@ -77,94 +77,45 @@ export default class AwaitBlock extends Node {
const { snippet } = this.expression;
const info = block.getUniqueName(`info`);
const promise = block.getUniqueName(`promise`);
const resolved = block.getUniqueName(`resolved`);
const await_block = block.getUniqueName(`await_block`);
const await_block_type = block.getUniqueName(`await_block_type`);
const token = block.getUniqueName(`token`);
const await_token = block.getUniqueName(`await_token`);
const handle_promise = block.getUniqueName(`handle_promise`);
const replace_await_block = block.getUniqueName(`replace_await_block`);
const old_block = block.getUniqueName(`old_block`);
const value = block.getUniqueName(`value`);
const error = block.getUniqueName(`error`);
const create_pending_block = this.pending.block.name;
const create_then_block = this.then.block.name;
const create_catch_block = this.catch.block.name;
block.addVariable(await_block);
block.addVariable(await_block_type);
block.addVariable(await_token);
block.addVariable(promise);
block.addVariable(resolved);
block.maintainContext = true;
const infoProps = [
block.alias('component') === 'component' ? 'component' : `component: #component`,
'ctx',
'current: null',
this.pending.block.name && `pending: ${this.pending.block.name}`,
this.then.block.name && `then: ${this.then.block.name}`,
this.catch.block.name && `catch: ${this.catch.block.name}`,
this.then.block.name && `value: '${this.value}'`,
this.catch.block.name && `error: '${this.error}'`
].filter(Boolean);
block.builders.init.addBlock(deindent`
let ${info} = {
${infoProps.join(',\n')}
};
`);
// the `#component.root.set({})` below is just a cheap way to flush
// any oncreate handlers. We could have a dedicated `flush()` method
// but it's probably not worth it
block.builders.init.addBlock(deindent`
function ${replace_await_block}(${token}, type, ctx) {
if (${token} !== ${await_token}) return;
var ${old_block} = ${await_block};
${await_block} = type && (${await_block_type} = type)(#component, ctx);
if (${old_block}) {
${old_block}.u();
${old_block}.d();
${await_block}.c();
${await_block}.m(${updateMountNode}, ${anchor});
#component.root.set({});
}
}
function ${handle_promise}(${promise}) {
var ${token} = ${await_token} = {};
if (@isPromise(${promise})) {
${promise}.then(function(${value}) {
${this.value ? deindent`
${resolved} = { ${this.value}: ${value} };
${replace_await_block}(${token}, ${create_then_block}, @assign(@assign({}, ctx), ${resolved}));
` : deindent`
${replace_await_block}(${token}, null, null);
`}
}, function (${error}) {
${this.error ? deindent`
${resolved} = { ${this.error}: ${error} };
${replace_await_block}(${token}, ${create_catch_block}, @assign(@assign({}, ctx), ${resolved}));
` : deindent`
${replace_await_block}(${token}, null, null);
`}
});
// if we previously had a then/catch block, destroy it
if (${await_block_type} !== ${create_pending_block}) {
${replace_await_block}(${token}, ${create_pending_block}, ctx);
return true;
}
} else {
${resolved} = { ${this.value}: ${promise} };
if (${await_block_type} !== ${create_then_block}) {
${replace_await_block}(${token}, ${create_then_block}, @assign(@assign({}, ctx), ${resolved}));
return true;
}
}
}
${handle_promise}(${promise} = ${snippet});
@handlePromise(${promise} = ${snippet}, ${info});
`);
block.builders.create.addBlock(deindent`
${await_block}.c();
${info}.block.c();
`);
if (parentNodes) {
block.builders.claim.addBlock(deindent`
${await_block}.l(${parentNodes});
${info}.block.l(${parentNodes});
`);
}
@ -172,7 +123,8 @@ export default class AwaitBlock extends Node {
const anchorNode = parentNode ? 'null' : 'anchor';
block.builders.mount.addBlock(deindent`
${await_block}.m(${initialMountNode}, ${anchorNode});
${info}.block.m(${initialMountNode}, ${info}.anchor = ${anchorNode});
${info}.mount = () => ${updateMountNode};
`);
const conditions = [];
@ -184,7 +136,11 @@ export default class AwaitBlock extends Node {
conditions.push(
`${promise} !== (${promise} = ${snippet})`,
`${handle_promise}(${promise}, ctx)`
`@handlePromise(${promise}, ${info})`
);
block.builders.update.addLine(
`${info}.ctx = ctx;`
);
if (this.pending.block.hasUpdateMethod) {
@ -192,30 +148,27 @@ export default class AwaitBlock extends Node {
if (${conditions.join(' && ')}) {
// nothing
} else {
${await_block}.p(changed, @assign(@assign({}, ctx), ${resolved}));
${info}.block.p(changed, @assign(@assign({}, ctx), ${info}.resolved));
}
`);
} else {
block.builders.update.addBlock(deindent`
if (${conditions.join(' && ')}) {
${await_block}.c();
${await_block}.m(${anchor}.parentNode, ${anchor});
}
${conditions.join(' && ')}
`);
}
block.builders.unmount.addBlock(deindent`
${await_block}.u();
${info}.block.u();
`);
block.builders.destroy.addBlock(deindent`
${await_token} = null;
${await_block}.d();
${info}.block.d();
${info} = null;
`);
[this.pending, this.then, this.catch].forEach(status => {
status.children.forEach(child => {
child.build(status.block, null,'nodes');
child.build(status.block, null, 'nodes');
});
});
}

@ -0,0 +1,46 @@
import { assign, isPromise } from './utils.js';
export function handlePromise(promise, info) {
var token = info.token = {};
function update(type, key, value) {
if (info.token !== token) return;
info.resolved = key && { [key]: value };
const child_ctx = assign(assign({}, info.ctx), info.resolved);
const block = type && (info.current = type)(info.component, child_ctx);
if (info.block) {
info.block.u();
info.block.d();
block.c();
block.m(info.mount(), info.anchor);
info.component.root.set({});
}
info.block = block;
}
if (isPromise(promise)) {
promise.then(value => {
update(info.then, info.value, value);
}, error => {
update(info.catch, info.error, error);
});
// if we previously had a then/catch block, destroy it
if (info.current !== info.pending) {
update(info.pending);
return true;
}
} else {
if (info.current !== info.then) {
update(info.then, info.value, promise);
return true;
}
info.resolved = { [info.value]: promise };
}
}

@ -1,5 +1,6 @@
import { assign } from './utils.js';
import { noop } from './utils.js';
export * from './await-block.js';
export * from './dom.js';
export * from './keyed-each.js';
export * from './spread.js';
@ -136,10 +137,6 @@ export function _unmount() {
if (this._fragment) this._fragment.u();
}
export function isPromise(value) {
return value && typeof value.then === 'function';
}
export var PENDING = {};
export var SUCCESS = {};
export var FAILURE = {};

@ -8,4 +8,8 @@ export function assign(tar, src) {
export function assignTrue(tar, src) {
for (var k in src) tar[k] = 1;
return tar;
}
export function isPromise(value) {
return value && typeof value.then === 'function';
}

@ -0,0 +1,47 @@
let fulfil;
let promise = new Promise(f => {
fulfil = f;
});
export default {
data: {
promise
},
html: `
<p>loading...</p>
`,
test(assert, component, target) {
fulfil(42);
return promise
.then(() => {
assert.htmlEqual(target.innerHTML, `
<p>loaded</p>
`);
promise = new Promise((f, r) => {
fulfil = f;
});
component.set({
promise
});
assert.htmlEqual(target.innerHTML, `
<p>loading...</p>
`);
fulfil(43);
return promise.then(() => {});
})
.then(() => {
assert.htmlEqual(target.innerHTML, `
<p>loaded</p>
`);
});
}
};

@ -0,0 +1,7 @@
{#await promise}
<p>loading...</p>
{:then value}
<p>loaded</p>
{:catch error}
<p>errored</p>
{/await}
Loading…
Cancel
Save