Skip to content

Commit

Permalink
handle await block promises in a shared helper
Browse files Browse the repository at this point in the history
  • Loading branch information
Rich-Harris committed May 5, 2018
1 parent 6aa254a commit 46c6b4b
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 64 deletions.
93 changes: 33 additions & 60 deletions src/compile/nodes/AwaitBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ 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`);
Expand All @@ -100,79 +101,47 @@ export default class AwaitBlock extends Node {

block.maintainContext = true;

const infoProps = [
block.alias('component') === 'component' ? 'component' : `component: #component`,
'ctx',
'current: null',
create_pending_block && `pending: ${create_pending_block}`,
create_then_block && `then: ${create_then_block}`,
create_catch_block && `catch: ${create_catch_block}`,
create_then_block && `value: '${this.value}'`,
create_catch_block && `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});
`);
}

const initialMountNode = parentNode || '#target';
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 = [];
Expand All @@ -184,33 +153,37 @@ 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) {
block.builders.update.addBlock(deindent`
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});
${info}.block.c();
${info}.block.m(${anchor}.parentNode, ${anchor});
}
`);
}

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();
`);

[this.pending, this.then, this.catch].forEach(status => {
Expand Down
55 changes: 55 additions & 0 deletions src/shared/await-block.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { assign, isPromise } from './utils.js';

export function handlePromise(promise, info, mount, anchor) {
var token = info.token = {};

function update(type) {
if (info.token !== token) return;

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 => {
if (info.value) {
info.resolved = { [info.value]: value };
update(info.then);
} else {
info.resolved = null;
update(null);
}
}, error => {
if (info.error) {
info.resolved = { [info.error]: error };
update(info.catch);
} else {
info.resolved = null;
update(null);
}
});

// if we previously had a then/catch block, destroy it
if (info.current !== info.pending) {
update(info.pending);
return true;
}
} else {
info.resolved = { [info.value]: promise };
if (info.current !== info.then) {
update(info.then);
return true;
}
}
}
5 changes: 1 addition & 4 deletions src/shared/index.js
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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 = {};
Expand Down
4 changes: 4 additions & 0 deletions src/shared/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 comments on commit 46c6b4b

Please sign in to comment.