avocado-old/packages/behavior/item/actions.js
2019-09-08 07:39:55 -05:00

138 lines
2.9 KiB
JavaScript

import {compose, EventEmitter, TickingPromise} from '@avocado/core';
import {Traversal} from './traversal';
import {Traversals} from './traversals';
const decorate = compose(
EventEmitter,
);
export class Actions extends decorate(Traversals) {
static type() {
return 'actions';
}
constructor() {
super();
this._index = 0;
this.pending = null;
}
emitFinished() {
this.emit('actionsFinished');
}
get index() {
return this._index;
}
set index(index) {
this._index = index;
}
get() {
return this;
}
tick(context, elapsed) {
if (this.traversals.length === 0) {
this.emitFinished();
return;
}
if (this.pending) {
if (this.pending instanceof TickingPromise) {
this.pending.tick(elapsed);
}
return;
}
// Actions execute immediately until a promise is made, or they're all
// executed.
while (true) {
const result = this.traversals[this.index].traverse(context);
if (result instanceof Promise) {
result.then(() => {
this.prologue();
});
result.catch((error) => {
console.error(error);
this.prologue();
});
this.pending = result;
break;
}
this.prologue();
if (0 === this.index) {
break;
}
}
}
parallel(context) {
const promises = [];
const results = [];
const tickingPromises = [];
for (let i = 0; i < this.traversals.length; i++) {
const traversal = this.traversals[i];
const result = traversal.traverse(context);
results.push(result);
if (result instanceof TickingPromise) {
tickingPromises.push(result);
result.then(() => {
tickingPromises.splice(tickingPromises.indexOf(result), 1);
});
}
else if (result instanceof Promise) {
promises.push(result);
}
}
if (tickingPromises.length > 0) {
return new TickingPromise(
(resolve) => {
resolve(Promise.all(results));
},
(elapsed) => {
for (let i = 0; i < tickingPromises.length; i++) {
tickingPromises[i].tick(elapsed);
}
},
);
}
if (promises.length > 0) {
return Promise.all(results);
}
return results;
}
prologue() {
this.pending = null;
if (0 === (this.index = (this.index + 1) % this.traversals.length)) {
this.emitFinished();
}
}
serial(context) {
// Immediate tick. Maybe we can avoid a promise?
this.tick(context, 0);
// If it's pending, we have to return a ticking promise.
if (this.pending) {
return this.tickingPromise(context);
}
}
tickingPromise(context) {
return new TickingPromise(
(resolve) => {
this.once('actionsFinished', () => {
resolve();
});
},
(elapsed) => {
this.tick(context, elapsed);
},
);
}
}