graphene/docs/playground/graphene-js/FunctionPromise.js

108 lines
3.4 KiB
JavaScript

//
// FunctionPromise: possibly-asynchronous function constructor.
//
// This is a prototype polyfill for a FunctionPromise object as described in:
//
// https://bugzilla.mozilla.org/show_bug.cgi?id=854627
//
// Where possible it will arrange for the function body to be parsed/compiled
// off of the main thread, with the function object returned asynchronously
// via a promise. The fallback implementation processes just falls back to
// the standard synchronous Function() constructor.
//
// It doesn't (yet) have the following features from the linked proposal:
//
// * ability to copy to different workers
// * ability to store in IndexedDB
//
function FunctionPromise(/* [args1[, args2[, ...argN]],], functionBody) */) {
var useFallback =
typeof window === "undefined" ||
window.FunctionPromise !== FunctionPromise ||
typeof document === "undefined" ||
typeof document.createElement === "undefined" ||
typeof document.head === "undefined" ||
typeof document.head.appendChild === "undefined" ||
typeof Blob === "undefined" ||
typeof URL === "undefined" ||
typeof URL.createObjectURL === "undefined";
var args = Array.prototype.slice.call(arguments);
// For the fallback case, we just use the normal Function constructor.
if (useFallback) {
try {
var fn = Function.apply(null, args);
return Promise.resolve(fn);
} catch (err) {
return Promise.reject(err);
}
}
// If we have all the necessary pieces, we can do this asynchronously
// by writing a <script> tag into the DOM.
var funcid = FunctionPromise._nextid++;
return new Promise(function(resolve, reject) {
try {
var funcSrc = [];
funcSrc.push("window.FunctionPromise._results[" + funcid + "]=");
funcSrc.push("function(");
if (args.length > 1) {
funcSrc.push(args[0]);
for (var i = 1; i < args.length - 1; i++) {
funcSrc.push(",");
funcSrc.push(args[i]);
}
}
funcSrc.push("){");
funcSrc.push(args[args.length - 1]);
funcSrc.push("}");
var dataUrl = URL.createObjectURL(new Blob(funcSrc));
var scriptTag = document.createElement("script");
var cleanup = function() {
URL.revokeObjectURL(dataUrl);
scriptTag.remove();
delete window.FunctionPromise._results[funcid];
}
scriptTag.onerror = function() {
reject(new Error("unknown error loading FunctionPromise"))
cleanup();
}
scriptTag.onload = function() {
if (window.FunctionPromise._results[funcid]) {
resolve(window.FunctionPromise._results[funcid]);
} else {
// No function, something must have gone wrong.
// Likely a syntax error in the function body string.
// Fall back to Function() constructor to surface it.
try {
Function.apply(null, args);
reject(new Error("unknown error fulfilling FunctionPromise"));
} catch (err) {
reject(err);
}
}
cleanup();
}
scriptTag.src = dataUrl;
document.head.appendChild(scriptTag);
} catch (err) {
reject(err);
}
});
}
FunctionPromise._nextid = 0;
FunctionPromise._results = {};
if (typeof module !== "undefined" && typeof module.exports !== "undefined") {
if (typeof Promise === "undefined") {
Promise = require('es6-promise').Promise;
}
module.exports = FunctionPromise;
}