Separate Promise implementation from polyfilling
This commit is contained in:
parent
1410e35456
commit
d283b24337
6 changed files with 110 additions and 95 deletions
|
|
@ -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
|
### 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))
|
- 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))
|
||||||
|
|
|
||||||
|
|
@ -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.
|
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
|
### Signature
|
||||||
|
|
|
||||||
1
index.js
1
index.js
|
|
@ -17,5 +17,6 @@ m.parseQueryString = require("./querystring/parse")
|
||||||
m.buildQueryString = require("./querystring/build")
|
m.buildQueryString = require("./querystring/build")
|
||||||
m.version = "bleeding-edge"
|
m.version = "bleeding-edge"
|
||||||
m.vnode = require("./render/vnode")
|
m.vnode = require("./render/vnode")
|
||||||
|
m.PromisePolyfill = require("./promise/polyfill")
|
||||||
|
|
||||||
module.exports = m
|
module.exports = m
|
||||||
|
|
|
||||||
98
promise/polyfill.js
Normal file
98
promise/polyfill.js
Normal file
|
|
@ -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
|
||||||
|
|
@ -1,99 +1,6 @@
|
||||||
"use strict"
|
"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 PromisePolyfill = require("./polyfill")
|
||||||
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)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
if (typeof window.Promise === "undefined") window.Promise = PromisePolyfill
|
if (typeof window.Promise === "undefined") window.Promise = PromisePolyfill
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
var o = require("../../ospec/ospec")
|
var o = require("../../ospec/ospec")
|
||||||
var callAsync = require("../../test-utils/callAsync")
|
var callAsync = require("../../test-utils/callAsync")
|
||||||
var Promise = require("../../promise/promise")
|
var Promise = require("../../promise/polyfill")
|
||||||
|
|
||||||
o.spec("promise", function() {
|
o.spec("promise", function() {
|
||||||
o.spec("constructor", function() {
|
o.spec("constructor", function() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue