Skip to content

Instantly share code, notes, and snippets.

@Akjosch
Last active May 1, 2020 11:41
Show Gist options
  • Save Akjosch/baa4ff7b69795b91e2161688ba6d55ae to your computer and use it in GitHub Desktop.
Save Akjosch/baa4ff7b69795b91e2161688ba6d55ae to your computer and use it in GitHub Desktop.
SugarCube <<when>> macro, delaying execution of the code block until a promise is resolved

Example usage:

<<set _promiseGen = function() {
    return new InspectablePromise(function(resolve, reject, progress) {
        setTimeout(function() { resolve(random(1, 100)); }, 2000);
    });
}>>
<<when _promise = _promiseGen()>>
    The value returned from the promise is <<= _promise.value>>.
<</when>>
/* This is a helper class, to make Promises you can easily pull the value from synchronously */
const InspectablePromise = (function() {
var statusProp = Symbol("status");
var progressProp = Symbol("progress");
var valueProp = Symbol("value");
var reasonProp = Symbol("reason");
/**
* Inspectable Promise
*/
class InspectablePromise extends Promise {
/**
* @param {(resolve: (value: any) => void, reject: (reason: any) => void, progress: (progress: any) => void) => void} executor
*/
constructor(executor) {
var self;
super((resolve, reject) => {
executor(
(value) => {
if(self) {
self[statusProp] = "resolved";
self[valueProp] = value;
resolve(value);
} else {
setTimeout(() => {
self[statusProp] = "resolved";
self[valueProp] = value;
resolve(value);
}, 0);
}
},
(reason) => {
if(self) {
self[statusProp] = "rejected";
self[reasonProp] = reason;
reject(reason);
} else {
setTimeout(() => {
self[statusProp] = "rejected";
self[reasonProp] = reason;
reject(reason);
}, 0);
}
},
(progress) => {
if(self) {
self[progressProp] = progress;
} else {
setTimeout(() => { self[progressProp] = progress; }, 0);
}
}
);
});
self = this;
this[statusProp] = "pending";
}
get status() { return this[statusProp]; }
get pending() { return this[statusProp] === "pending"; }
get resolved() { return this[statusProp] === "resolved"; }
get rejected() { return this[statusProp] === "rejected"; }
get value() { return this[valueProp]; }
get reason() { return this[reasonProp]; }
get progress() { return this[progressProp]; }
}
return InspectablePromise;
})();
Macro.add("when", {
skipArgs: true,
isAsync: true,
tags: ["otherwise"],
/**
* @param {Node} target
* @param {String} content
*/
_drawContent: function(target, content) {
if(content) {
const result = document.createDocumentFragment();
jQuery(result).wiki(content);
if(result.hasChildNodes()) {
target.parentNode.replaceChild(result, target);
}
}
},
handler: function() {
if(this.args.full.length === 0) {
return this.error('no promise expression specified');
}
if(this.payload.length > 2) {
return this.error('only one <<otherwise>> tag is allowed');
}
try {
/** @type {string} */
const content = this.payload[0].contents;
/** @type {string} */
const errorContent = this.payload.length > 1 ? this.payload[1].contents : undefined;
/** @type {string} */
const errorVar = this.payload.length > 1 ? this.payload[1].args.raw : undefined;
/** @type {Node} */
const target = document.createElement("when");
this.output.appendChild(target);
/** @type {(parent:Node, sibling?:ChildNode, content?:string) => void} */
const _drawContent = this.self._drawContent;
Promise.resolve(Scripting.evalJavaScript(this.args.full)).then(function() {
_drawContent(target, content);
}, function(error) {
if(errorVar) {
State.setVar(errorVar, error);
}
_drawContent(target, errorContent);
});
} catch(ex) {
return this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);
}
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment