From d283b243371ec5e440fb05e9689944b5340663fe Mon Sep 17 00:00:00 2001 From: spacejack Date: Mon, 19 Feb 2018 16:28:28 -0500 Subject: [PATCH] Separate Promise implementation from polyfilling --- docs/change-log.md | 7 +++ docs/promise.md | 2 + index.js | 1 + promise/polyfill.js | 98 +++++++++++++++++++++++++++++++++++ promise/promise.js | 95 +-------------------------------- promise/tests/test-promise.js | 2 +- 6 files changed, 110 insertions(+), 95 deletions(-) create mode 100644 promise/polyfill.js diff --git a/docs/change-log.md b/docs/change-log.md index 5df143f6..f49329ec 100644 --- a/docs/change-log.md +++ b/docs/change-log.md @@ -47,6 +47,13 @@ --- +### v1.1.7 + +- Promise polyfill implementation separated from polyfilling logic. +- `PromisePolyfill` is now available on the exported/global `m`. + +--- + ### v1.1.6 - core: render() function can no longer prevent from changing `document.activeElement` in lifecycle hooks ([#1988](https://github.com/MithrilJS/mithril.js/pull/1988), [@purplecode](https://github.com/purplecode)) diff --git a/docs/promise.md b/docs/promise.md index 8091c2e2..dc93f92d 100644 --- a/docs/promise.md +++ b/docs/promise.md @@ -26,6 +26,8 @@ A [ES6 Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/G A Promise is a mechanism for working with asynchronous computations. +Mithril provides a polyfill when the environment does not support Promises. The polyfill can also be referenced specifically via `m.PromisePolyfill`. + --- ### Signature diff --git a/index.js b/index.js index 6e69cea4..123ba4ca 100644 --- a/index.js +++ b/index.js @@ -17,5 +17,6 @@ m.parseQueryString = require("./querystring/parse") m.buildQueryString = require("./querystring/build") m.version = "bleeding-edge" m.vnode = require("./render/vnode") +m.PromisePolyfill = require("./promise/polyfill") module.exports = m diff --git a/promise/polyfill.js b/promise/polyfill.js new file mode 100644 index 00000000..4d990dcd --- /dev/null +++ b/promise/polyfill.js @@ -0,0 +1,98 @@ +"use strict" +/** @constructor */ +var PromisePolyfill = function(executor) { + if (!(this instanceof PromisePolyfill)) throw new Error("Promise must be called with `new`") + if (typeof executor !== "function") throw new TypeError("executor must be a function") + + var self = this, resolvers = [], rejectors = [], resolveCurrent = handler(resolvers, true), rejectCurrent = handler(rejectors, false) + var instance = self._instance = {resolvers: resolvers, rejectors: rejectors} + var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout + function handler(list, shouldAbsorb) { + return function execute(value) { + var then + try { + if (shouldAbsorb && value != null && (typeof value === "object" || typeof value === "function") && typeof (then = value.then) === "function") { + if (value === self) throw new TypeError("Promise can't be resolved w/ itself") + executeOnce(then.bind(value)) + } + else { + callAsync(function() { + if (!shouldAbsorb && list.length === 0) console.error("Possible unhandled promise rejection:", value) + for (var i = 0; i < list.length; i++) list[i](value) + resolvers.length = 0, rejectors.length = 0 + instance.state = shouldAbsorb + instance.retry = function() {execute(value)} + }) + } + } + catch (e) { + rejectCurrent(e) + } + } + } + function executeOnce(then) { + var runs = 0 + function run(fn) { + return function(value) { + if (runs++ > 0) return + fn(value) + } + } + var onerror = run(rejectCurrent) + try {then(run(resolveCurrent), onerror)} catch (e) {onerror(e)} + } + + executeOnce(executor) +} +PromisePolyfill.prototype.then = function(onFulfilled, onRejection) { + var self = this, instance = self._instance + function handle(callback, list, next, state) { + list.push(function(value) { + if (typeof callback !== "function") next(value) + else try {resolveNext(callback(value))} catch (e) {if (rejectNext) rejectNext(e)} + }) + if (typeof instance.retry === "function" && state === instance.state) instance.retry() + } + var resolveNext, rejectNext + var promise = new PromisePolyfill(function(resolve, reject) {resolveNext = resolve, rejectNext = reject}) + handle(onFulfilled, instance.resolvers, resolveNext, true), handle(onRejection, instance.rejectors, rejectNext, false) + return promise +} +PromisePolyfill.prototype.catch = function(onRejection) { + return this.then(null, onRejection) +} +PromisePolyfill.resolve = function(value) { + if (value instanceof PromisePolyfill) return value + return new PromisePolyfill(function(resolve) {resolve(value)}) +} +PromisePolyfill.reject = function(value) { + return new PromisePolyfill(function(resolve, reject) {reject(value)}) +} +PromisePolyfill.all = function(list) { + return new PromisePolyfill(function(resolve, reject) { + var total = list.length, count = 0, values = [] + if (list.length === 0) resolve([]) + else for (var i = 0; i < list.length; i++) { + (function(i) { + function consume(value) { + count++ + values[i] = value + if (count === total) resolve(values) + } + if (list[i] != null && (typeof list[i] === "object" || typeof list[i] === "function") && typeof list[i].then === "function") { + list[i].then(consume, reject) + } + else consume(list[i]) + })(i) + } + }) +} +PromisePolyfill.race = function(list) { + return new PromisePolyfill(function(resolve, reject) { + for (var i = 0; i < list.length; i++) { + list[i].then(resolve, reject) + } + }) +} + +module.exports = PromisePolyfill diff --git a/promise/promise.js b/promise/promise.js index 53aefecc..1f19bc33 100644 --- a/promise/promise.js +++ b/promise/promise.js @@ -1,99 +1,6 @@ "use strict" -/** @constructor */ -var PromisePolyfill = function(executor) { - if (!(this instanceof PromisePolyfill)) throw new Error("Promise must be called with `new`") - if (typeof executor !== "function") throw new TypeError("executor must be a function") - var self = this, resolvers = [], rejectors = [], resolveCurrent = handler(resolvers, true), rejectCurrent = handler(rejectors, false) - var instance = self._instance = {resolvers: resolvers, rejectors: rejectors} - var callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout - function handler(list, shouldAbsorb) { - return function execute(value) { - var then - try { - if (shouldAbsorb && value != null && (typeof value === "object" || typeof value === "function") && typeof (then = value.then) === "function") { - if (value === self) throw new TypeError("Promise can't be resolved w/ itself") - executeOnce(then.bind(value)) - } - else { - callAsync(function() { - if (!shouldAbsorb && list.length === 0) console.error("Possible unhandled promise rejection:", value) - for (var i = 0; i < list.length; i++) list[i](value) - resolvers.length = 0, rejectors.length = 0 - instance.state = shouldAbsorb - instance.retry = function() {execute(value)} - }) - } - } - catch (e) { - rejectCurrent(e) - } - } - } - function executeOnce(then) { - var runs = 0 - function run(fn) { - return function(value) { - if (runs++ > 0) return - fn(value) - } - } - var onerror = run(rejectCurrent) - try {then(run(resolveCurrent), onerror)} catch (e) {onerror(e)} - } - - executeOnce(executor) -} -PromisePolyfill.prototype.then = function(onFulfilled, onRejection) { - var self = this, instance = self._instance - function handle(callback, list, next, state) { - list.push(function(value) { - if (typeof callback !== "function") next(value) - else try {resolveNext(callback(value))} catch (e) {if (rejectNext) rejectNext(e)} - }) - if (typeof instance.retry === "function" && state === instance.state) instance.retry() - } - var resolveNext, rejectNext - var promise = new PromisePolyfill(function(resolve, reject) {resolveNext = resolve, rejectNext = reject}) - handle(onFulfilled, instance.resolvers, resolveNext, true), handle(onRejection, instance.rejectors, rejectNext, false) - return promise -} -PromisePolyfill.prototype.catch = function(onRejection) { - return this.then(null, onRejection) -} -PromisePolyfill.resolve = function(value) { - if (value instanceof PromisePolyfill) return value - return new PromisePolyfill(function(resolve) {resolve(value)}) -} -PromisePolyfill.reject = function(value) { - return new PromisePolyfill(function(resolve, reject) {reject(value)}) -} -PromisePolyfill.all = function(list) { - return new PromisePolyfill(function(resolve, reject) { - var total = list.length, count = 0, values = [] - if (list.length === 0) resolve([]) - else for (var i = 0; i < list.length; i++) { - (function(i) { - function consume(value) { - count++ - values[i] = value - if (count === total) resolve(values) - } - if (list[i] != null && (typeof list[i] === "object" || typeof list[i] === "function") && typeof list[i].then === "function") { - list[i].then(consume, reject) - } - else consume(list[i]) - })(i) - } - }) -} -PromisePolyfill.race = function(list) { - return new PromisePolyfill(function(resolve, reject) { - for (var i = 0; i < list.length; i++) { - list[i].then(resolve, reject) - } - }) -} +var PromisePolyfill = require("./polyfill") if (typeof window !== "undefined") { if (typeof window.Promise === "undefined") window.Promise = PromisePolyfill diff --git a/promise/tests/test-promise.js b/promise/tests/test-promise.js index e368165c..2905d694 100644 --- a/promise/tests/test-promise.js +++ b/promise/tests/test-promise.js @@ -2,7 +2,7 @@ var o = require("../../ospec/ospec") var callAsync = require("../../test-utils/callAsync") -var Promise = require("../../promise/promise") +var Promise = require("../../promise/polyfill") o.spec("promise", function() { o.spec("constructor", function() {