Kick promise polyfill
This commit is contained in:
parent
517661a606
commit
e9a365c150
20 changed files with 41 additions and 1388 deletions
|
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
## What is Mithril.js?
|
## What is Mithril.js?
|
||||||
|
|
||||||
A modern client-side JavaScript framework for building Single Page Applications. It's small (<!-- size -->10.05 KB<!-- /size --> gzipped), fast and provides routing and XHR utilities out of the box.
|
A modern client-side JavaScript framework for building Single Page Applications. It's small (<!-- size -->9.34 KB<!-- /size --> gzipped), fast and provides routing and XHR utilities out of the box.
|
||||||
|
|
||||||
Mithril.js is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍.
|
Mithril.js is used by companies like Vimeo and Nike, and open source platforms like Lichess 👍.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
var Vnode = require("../render/vnode")
|
var Vnode = require("../render/vnode")
|
||||||
var m = require("../render/hyperscript")
|
var m = require("../render/hyperscript")
|
||||||
var Promise = require("../promise/promise")
|
|
||||||
|
|
||||||
var buildPathname = require("../pathname/build")
|
var buildPathname = require("../pathname/build")
|
||||||
var parsePathname = require("../pathname/parse")
|
var parsePathname = require("../pathname/parse")
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ var m = require("../../render/hyperscript")
|
||||||
var coreRenderer = require("../../render/render")
|
var coreRenderer = require("../../render/render")
|
||||||
var apiMountRedraw = require("../../api/mount-redraw")
|
var apiMountRedraw = require("../../api/mount-redraw")
|
||||||
var apiRouter = require("../../api/router")
|
var apiRouter = require("../../api/router")
|
||||||
var Promise = require("../../promise/promise")
|
|
||||||
|
|
||||||
o.spec("route", function() {
|
o.spec("route", function() {
|
||||||
// Note: the `n` parameter used in calls to this are generally found by
|
// Note: the `n` parameter used in calls to this are generally found by
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ var FancyComponent = {
|
||||||
|
|
||||||
`vnode.dom` points to the root DOM element of the component (`<div class="fancy">`). We use the classList API here to add an `exit` class to `<div class="fancy">`.
|
`vnode.dom` points to the root DOM element of the component (`<div class="fancy">`). We use the classList API here to add an `exit` class to `<div class="fancy">`.
|
||||||
|
|
||||||
Then we return a [Promise](promise.md) that resolves when the `animationend` event fires. When we return a promise from `onbeforeremove`, Mithril.js waits until the promise is resolved and only then it removes the element. In this case, it waits for the exit animation to finish.
|
Then we return a Promise that resolves when the `animationend` event fires. When we return a promise from `onbeforeremove`, Mithril.js waits until the promise is resolved and only then it removes the element. In this case, it waits for the exit animation to finish.
|
||||||
|
|
||||||
We can verify that both the enter and exit animations work by mounting the `Toggler` component:
|
We can verify that both the enter and exit animations work by mounting the `Toggler` component:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@
|
||||||
- [m.trust](trust.md)
|
- [m.trust](trust.md)
|
||||||
- [m.fragment](fragment.md)
|
- [m.fragment](fragment.md)
|
||||||
- [m.redraw](redraw.md)
|
- [m.redraw](redraw.md)
|
||||||
- [Promise](promise.md)
|
|
||||||
- Optional
|
- Optional
|
||||||
- [Stream](stream.md)
|
- [Stream](stream.md)
|
||||||
- Tooling
|
- Tooling
|
||||||
|
|
|
||||||
315
docs/promise.md
315
docs/promise.md
|
|
@ -1,315 +0,0 @@
|
||||||
<!--meta-description
|
|
||||||
Documentation on Mithril.js' Promise polyfill
|
|
||||||
-->
|
|
||||||
|
|
||||||
# Promise(executor)
|
|
||||||
|
|
||||||
- [Description](#description)
|
|
||||||
- [Signature](#signature)
|
|
||||||
- [Static members](#static-members)
|
|
||||||
- [Promise.resolve](#promiseresolve)
|
|
||||||
- [Promise.reject](#promisereject)
|
|
||||||
- [Promise.all](#promiseall)
|
|
||||||
- [Promise.race](#promiserace)
|
|
||||||
- [Instance members](#instance-members)
|
|
||||||
- [promise.then](#promisethen)
|
|
||||||
- [promise.catch](#promisecatch)
|
|
||||||
- [How it works](#how-it-works)
|
|
||||||
- [Promise chaining](#promise-chaining)
|
|
||||||
- [Promise absorption](#promise-absorption)
|
|
||||||
- [Error handling](#error-handling)
|
|
||||||
- [Shorthands](#shorthands)
|
|
||||||
- [Multiple promises](#multiple-promises)
|
|
||||||
- [Why not callbacks](#why-not-callbacks)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Description
|
|
||||||
|
|
||||||
An [ES6 Promise](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) polyfill.
|
|
||||||
|
|
||||||
A Promise is a mechanism for working with asynchronous computations.
|
|
||||||
|
|
||||||
Mithril.js provides a polyfill when the environment does not support Promises. The polyfill can also be referenced specifically via `m.PromisePolyfill`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Signature
|
|
||||||
|
|
||||||
`promise = new Promise(executor)`
|
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
|
||||||
----------- | ----------------------------- | -------- | ---
|
|
||||||
`executor` | `(Function, Function) -> any` | Yes | A function that determines how the promise will be resolved or rejected
|
|
||||||
**returns** | `Promise` | | Returns a promise
|
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
##### executor
|
|
||||||
|
|
||||||
`executor(resolve, reject)`
|
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
|
||||||
----------- | ----------------------------- | -------- | ---
|
|
||||||
`resolve` | `any -> any` | No | Call this function to resolve the promise
|
|
||||||
`reject` | `any -> any` | No | Call this function to reject the promise
|
|
||||||
**returns** | | | The return value is ignored
|
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Static members
|
|
||||||
|
|
||||||
##### Promise.resolve
|
|
||||||
|
|
||||||
`promise = Promise.resolve(value)`
|
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
|
||||||
----------- | ----------------------------- | -------- | ---
|
|
||||||
`value` | `any` | No | A value to resolve to
|
|
||||||
**returns** | `Promise` | | A promise resolved to `value`
|
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
##### Promise.reject
|
|
||||||
|
|
||||||
`promise = Promise.reject(value)`
|
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
|
||||||
----------- | ----------------------------- | -------- | ---
|
|
||||||
`value` | `any` | No | A value to reject as
|
|
||||||
**returns** | `Promise` | | A rejected promise with `value` as its reason
|
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
##### Promise.all
|
|
||||||
|
|
||||||
`promise = Promise.all(promises)`
|
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
|
||||||
----------- | ----------------------------- | -------- | ---
|
|
||||||
`promises` | `Array<Promise|any>` | Yes | A list of promises to wait for. If an item is not a promise, it's equivalent to calling `Promise.resolve` on it
|
|
||||||
**returns** | `Promise` | | A promise that resolves only after all `promises` resolve, or rejects if any of them are rejected.
|
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
##### Promise.race
|
|
||||||
|
|
||||||
`promise = Promise.race(promises)`
|
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
|
||||||
----------- | ----------------------------- | -------- | ---
|
|
||||||
`promises` | `Array<Promise|any>` | Yes | A list of promises to wait for. If an item is not a promise, it's equivalent to calling `Promise.resolve` on it
|
|
||||||
**returns** | `Promise` | | A promise that resolves as soon as one of the `promises` is resolved or rejected.
|
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Instance members
|
|
||||||
|
|
||||||
##### promise.then
|
|
||||||
|
|
||||||
`nextPromise = promise.then(onFulfilled, onRejected)`
|
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
|
||||||
------------- | ----------------------- | -------- | ---
|
|
||||||
`onFulfilled` | `any -> (any|Promise)` | No | A function that is called if the promise is resolved. The first parameter of this function is the value that this promise was resolved with. If the return value of this function is not a Promise, it is used as the value for resolving `nextPromise`. If the returned value is a Promise, the value of `nextPromise` depends on the inner Promise's status. If this function throws, `nextPromise` is rejected with the error as its reason. If `onFulfilled` is `null`, it's ignored
|
|
||||||
`onRejected` | `any -> (any|Promise)` | No | A function that is called when the promise is rejected. The first parameter of this function is a value that represents the reason why the promise was rejected. If the return value of this function is not a Promise, it is used as the value for resolving `nextPromise`. If the returned value is a Promise, then value of `nextPromise` depends on the inner Promise's status. If this function throws, `nextPromise` is rejected with the error as its reason. If `onRejected` is `null`, it's ignored
|
|
||||||
**returns** | `Promise` | | A promise whose value depends on the status of the current promise
|
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
##### promise.catch
|
|
||||||
|
|
||||||
`nextPromise = promise.catch(onRejected)`
|
|
||||||
|
|
||||||
Argument | Type | Required | Description
|
|
||||||
------------- | ----------------------- | -------- | ---
|
|
||||||
`onRejected` | `any -> (any|Promise)` | No | A function that is called when the promise is rejected. The first parameter of this function is a value that represents the reason why the promise was rejected. If the return value of this function is not a Promise, it is used as the value for resolving `nextPromise`. If the returned value is a Promise, then value of `nextPromise` depends on the inner Promise's status. If this function throws, `nextPromise` is rejected with the error as its reason. If `onRejected` is `null`, it's ignored
|
|
||||||
**returns** | `Promise` | | A promise whose value depends on the status of the current promise
|
|
||||||
|
|
||||||
[How to read signatures](signatures.md)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### How it works
|
|
||||||
|
|
||||||
A Promise is an object that represents a value which may be available in the future
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// this promise resolves after one second
|
|
||||||
var promise = new Promise(function(resolve, reject) {
|
|
||||||
setTimeout(function() {
|
|
||||||
resolve("hello")
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
// logs "hello" after one second
|
|
||||||
console.log(value)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Promises are useful for working with asynchronous APIs, such as [`m.request`](request.md)
|
|
||||||
|
|
||||||
Asynchronous APIs are those which typically take a long time to run, and therefore would take too long to return a value using the `return` statement of a function. Instead, they do their work in the background, allowing other JavaScript code to run in the meantime. When they are done, they call a function with their results.
|
|
||||||
|
|
||||||
The `m.request` function takes time to run because it makes an HTTP request to a remote server and has to wait for a response, which may take several milliseconds due to network latency.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Promise chaining
|
|
||||||
|
|
||||||
Promises can be chained. Returning a value from a `then` callback makes it available as the argument to the next `then` callback. This allows refactoring code into smaller functions
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function getUsers() {return m.request("/api/v1/users")}
|
|
||||||
|
|
||||||
// AVOID: hard to test god functions
|
|
||||||
getUsers().then(function(users) {
|
|
||||||
var firstTen = users.slice(0, 9)
|
|
||||||
var firstTenNames = firstTen.map(function(user) {return user.firstName + " " + user.lastName})
|
|
||||||
alert(firstTenNames)
|
|
||||||
})
|
|
||||||
|
|
||||||
// PREFER: easy to test small functions
|
|
||||||
function getFirstTen(items) {return items.slice(0, 9)}
|
|
||||||
function getUserName(user) {return user.firstName + " " + user.lastName}
|
|
||||||
function getUserNames(users) {return users.map(getUserName)}
|
|
||||||
|
|
||||||
getUsers()
|
|
||||||
.then(getFirstTen)
|
|
||||||
.then(getUserNames)
|
|
||||||
.then(alert)
|
|
||||||
```
|
|
||||||
|
|
||||||
In the refactored code, `getUsers()` returns a promise, and we chain three callbacks. When `getUsers()` resolves, the `getFirstTen` function is called with a list of users as its first argument. This function returns a list of ten items. `getUserNames` returns a list of names for the 10 items that were passed as the argument to it. Finally, the list of names is alerted.
|
|
||||||
|
|
||||||
In the original code above, it's very difficult to test the god function since you must make an HTTP request to run the code, and there's an `alert()` call at the end of the function
|
|
||||||
|
|
||||||
In the refactored version, it's trivial to test whether `getFirstTen` has any off-by-one errors, or whether we forgot to add a space between the first and last names in `getUserName`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Promise absorption
|
|
||||||
|
|
||||||
Promises absorb other promises. Basically, this means you can never receive a Promise as an argument to `onFulfilled` or `onRejected` callbacks for `then` and `catch` methods. This feature allows us to flatten nested promises to make code more manageable.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
function searchUsers(q) {return m.request("/api/v1/users/search", {params: {q: q}})}
|
|
||||||
function getUserProjects(id) {return m.request("/api/v1/users/" + id + "/projects")}
|
|
||||||
|
|
||||||
// AVOID: pyramid of doom
|
|
||||||
searchUsers("John").then(function(users) {
|
|
||||||
getUserProjects(users[0].id).then(function(projects) {
|
|
||||||
var titles = projects.map(function(project) {return project.title})
|
|
||||||
alert(titles)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// PREFER: flat code flow
|
|
||||||
function getFirstId(items) {return items[0].id}
|
|
||||||
function getProjectTitles(projects) {return projects.map(getProjectTitle)}
|
|
||||||
function getProjectTitle(project) {return project.title}
|
|
||||||
|
|
||||||
searchUsers("John")
|
|
||||||
.then(getFirstId)
|
|
||||||
.then(getUserProjects)
|
|
||||||
.then(getProjectTitles)
|
|
||||||
.then(alert)
|
|
||||||
```
|
|
||||||
|
|
||||||
In the refactored code, `getFirstId` returns an id, which is passed as the first argument to `getUserProjects`. That, in turn, returns a promise that resolves to a list of projects. This promise is absorbed, so the first argument to `getProjectTitles` is not a promise, but the list of projects. `getProjectTitles` returns a list of titles, and that list is finally alerted.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Error handling
|
|
||||||
|
|
||||||
Promises can propagate errors to appropriate handlers.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
searchUsers("John")
|
|
||||||
.then(getFirstId)
|
|
||||||
.then(getUserProjects)
|
|
||||||
.then(getProjectTitles)
|
|
||||||
.then(alert)
|
|
||||||
.catch(function(e) {
|
|
||||||
console.log(e)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
Here's the previous example with error handling. The `searchUsers` function could fail if the network was offline, resulting in an error. In that case, none of the `.then` callbacks would be triggered, and the `.catch` callback would log the error to console.
|
|
||||||
|
|
||||||
If the request in `getUserProjects` failed, then similarly, `getProjectTitles` and `alert` would not be called. Again, the `.catch` callback would log the error.
|
|
||||||
|
|
||||||
The error handler would also catch a null reference exception if `searchUsers` returned no results, and `getFirstId` attempted to access the `id` property of a non-existent array item.
|
|
||||||
|
|
||||||
Thanks to these error propagation semantics, it's easy to keep each function small and testable without sprinkling `try`/`catch` blocks everywhere.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Shorthands
|
|
||||||
|
|
||||||
Sometimes, you already have a value, but want to wrap it in a Promise. It's for this purpose that `Promise.resolve` and `Promise.reject` exist.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// suppose this list came from localStorage
|
|
||||||
var users = [{id: 1, firstName: "John", lastName: "Doe"}]
|
|
||||||
|
|
||||||
// in that case, `users` may or may not exist depending on whether there was data in localStorage
|
|
||||||
var promise = users ? Promise.resolve(users) : getUsers()
|
|
||||||
promise
|
|
||||||
.then(getFirstTen)
|
|
||||||
.then(getUserNames)
|
|
||||||
.then(alert)
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Multiple promises
|
|
||||||
|
|
||||||
In some occasions, you may need to make HTTP requests in parallel, and run code after all requests complete. This can be accomplished by `Promise.all`
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
Promise.all([
|
|
||||||
searchUsers("John"),
|
|
||||||
searchUsers("Mary"),
|
|
||||||
])
|
|
||||||
.then(function(data) {
|
|
||||||
// data[0] is an array of users whose names are John
|
|
||||||
// data[1] is an array of users whose names are Mary
|
|
||||||
|
|
||||||
// the returned value is equivalent to [
|
|
||||||
// getUserNames(data[0]),
|
|
||||||
// getUserNames(data[1]),
|
|
||||||
// ]
|
|
||||||
return data.map(getUserNames)
|
|
||||||
})
|
|
||||||
.then(alert)
|
|
||||||
```
|
|
||||||
|
|
||||||
In the example above, there are two user searches happening in parallel. Once they both complete, we take the names of all the users and alert them.
|
|
||||||
|
|
||||||
This example also illustrates another benefit of smaller functions: we reused the `getUserNames` function we had created above.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Why not callbacks
|
|
||||||
|
|
||||||
Callbacks are another mechanism for working with asynchronous computations, and are indeed more adequate to use if an asynchronous computation may occur more than one time (for example, an `onscroll` event handler).
|
|
||||||
|
|
||||||
However, for asynchronous computations that only occur once in response to an action, promises can be refactored more effectively, reducing code smells known as pyramids of doom (deeply nested series of callbacks with unmanaged state being used across several closure levels).
|
|
||||||
|
|
||||||
In addition, promises can considerably reduce boilerplate related to error handling.
|
|
||||||
|
|
@ -25,7 +25,7 @@ Documentation on m.request(), a utility for making XHR/AJAX requests
|
||||||
|
|
||||||
### Description
|
### Description
|
||||||
|
|
||||||
Makes XHR (aka AJAX) requests, and returns a [promise](promise.md)
|
Makes XHR (aka AJAX) requests, and returns a promise
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
m.request({
|
m.request({
|
||||||
|
|
@ -95,7 +95,7 @@ m.request({
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
A call to `m.request` returns a [promise](promise.md) and triggers a redraw upon completion of its promise chain.
|
A call to `m.request` returns a promise and triggers a redraw upon completion of its promise chain.
|
||||||
|
|
||||||
By default, `m.request` assumes the response is in JSON format and parses it into a JavaScript object (or array).
|
By default, `m.request` assumes the response is in JSON format and parses it into a JavaScript object (or array).
|
||||||
|
|
||||||
|
|
|
||||||
1
index.js
1
index.js
|
|
@ -20,7 +20,6 @@ m.buildQueryString = require("./querystring/build")
|
||||||
m.parsePathname = require("./pathname/parse")
|
m.parsePathname = require("./pathname/parse")
|
||||||
m.buildPathname = require("./pathname/build")
|
m.buildPathname = require("./pathname/build")
|
||||||
m.vnode = require("./render/vnode")
|
m.vnode = require("./render/vnode")
|
||||||
m.PromisePolyfill = require("./promise/polyfill")
|
|
||||||
m.censor = require("./util/censor")
|
m.censor = require("./util/censor")
|
||||||
|
|
||||||
module.exports = m
|
module.exports = m
|
||||||
|
|
|
||||||
189
mithril.js
189
mithril.js
|
|
@ -159,131 +159,7 @@ hyperscript.fragment = function() {
|
||||||
vnode2.children = Vnode.normalizeChildren(vnode2.children)
|
vnode2.children = Vnode.normalizeChildren(vnode2.children)
|
||||||
return vnode2
|
return vnode2
|
||||||
}
|
}
|
||||||
/* global window */
|
var _11 = function($window) {
|
||||||
/** @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 with 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.prototype.finally = function(callback) {
|
|
||||||
return this.then(
|
|
||||||
function(value) {
|
|
||||||
return PromisePolyfill.resolve(callback()).then(function() {
|
|
||||||
return value
|
|
||||||
})
|
|
||||||
},
|
|
||||||
function(reason) {
|
|
||||||
return PromisePolyfill.resolve(callback()).then(function() {
|
|
||||||
return PromisePolyfill.reject(reason);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
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.Promise === "undefined") {
|
|
||||||
window.Promise = PromisePolyfill
|
|
||||||
} else if (!window.Promise.prototype.finally) {
|
|
||||||
window.Promise.prototype.finally = PromisePolyfill.prototype.finally
|
|
||||||
}
|
|
||||||
var PromisePolyfill = window.Promise
|
|
||||||
} else if (typeof global !== "undefined") {
|
|
||||||
if (typeof global.Promise === "undefined") {
|
|
||||||
global.Promise = PromisePolyfill
|
|
||||||
} else if (!global.Promise.prototype.finally) {
|
|
||||||
global.Promise.prototype.finally = PromisePolyfill.prototype.finally
|
|
||||||
}
|
|
||||||
var PromisePolyfill = global.Promise
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
var _13 = function($window) {
|
|
||||||
var $doc = $window && $window.document
|
var $doc = $window && $window.document
|
||||||
var currentRedraw
|
var currentRedraw
|
||||||
var nameSpace = {
|
var nameSpace = {
|
||||||
|
|
@ -480,7 +356,7 @@ var _13 = function($window) {
|
||||||
// 3) remove the nodes present in the old list, but absent in the new one
|
// 3) remove the nodes present in the old list, but absent in the new one
|
||||||
// 4) figure out what nodes in 1) to move in order to minimize the DOM operations.
|
// 4) figure out what nodes in 1) to move in order to minimize the DOM operations.
|
||||||
//
|
//
|
||||||
// To achieve 1) one can create a dictionary of keys => index (for the old list), then0 iterate
|
// To achieve 1) one can create a dictionary of keys => index (for the old list), then iterate
|
||||||
// over the new list and for each new vnode3, find the corresponding vnode3 in the old list using
|
// over the new list and for each new vnode3, find the corresponding vnode3 in the old list using
|
||||||
// the map.
|
// the map.
|
||||||
// 2) is achieved in the same step: if a new node has no corresponding entry in the map, it is new
|
// 2) is achieved in the same step: if a new node has no corresponding entry in the map, it is new
|
||||||
|
|
@ -526,7 +402,7 @@ var _13 = function($window) {
|
||||||
//
|
//
|
||||||
// In most scenarios `updateNode()` and `createNode()` perform the DOM operations. However,
|
// In most scenarios `updateNode()` and `createNode()` perform the DOM operations. However,
|
||||||
// this is not the case if the node moved (second and fourth part of the diff algo). We move
|
// this is not the case if the node moved (second and fourth part of the diff algo). We move
|
||||||
// the old DOM nodes before updateNode runs0 because it enables us to use the cached `nextSibling`
|
// the old DOM nodes before updateNode runs because it enables us to use the cached `nextSibling`
|
||||||
// variable rather than fetching it using `getNextSibling()`.
|
// variable rather than fetching it using `getNextSibling()`.
|
||||||
//
|
//
|
||||||
// The fourth part of the diff currently inserts nodes unconditionally, leading to issues
|
// The fourth part of the diff currently inserts nodes unconditionally, leading to issues
|
||||||
|
|
@ -811,7 +687,7 @@ var _13 = function($window) {
|
||||||
}
|
}
|
||||||
// This covers a really specific edge case:
|
// This covers a really specific edge case:
|
||||||
// - Parent node is keyed and contains child
|
// - Parent node is keyed and contains child
|
||||||
// - Child is removed, returns unresolved promise0 in `onbeforeremove`
|
// - Child is removed, returns unresolved promise in `onbeforeremove`
|
||||||
// - Parent node is moved in keyed diff
|
// - Parent node is moved in keyed diff
|
||||||
// - Remaining children2 still need moved appropriately
|
// - Remaining children2 still need moved appropriately
|
||||||
//
|
//
|
||||||
|
|
@ -1127,7 +1003,7 @@ var _13 = function($window) {
|
||||||
// with a `handleEvent` method.
|
// with a `handleEvent` method.
|
||||||
// 3. The object does not inherit from `Object.prototype`, to avoid
|
// 3. The object does not inherit from `Object.prototype`, to avoid
|
||||||
// any potential interference with that (e.g. setters).
|
// any potential interference with that (e.g. setters).
|
||||||
// 4. The event name is remapped to the handler0 before calling it.
|
// 4. The event name is remapped to the handler before calling it.
|
||||||
// 5. In function-based event handlers, `ev.target === this`. We replicate
|
// 5. In function-based event handlers, `ev.target === this`. We replicate
|
||||||
// that below.
|
// that below.
|
||||||
// 6. In function-based event handlers, `return false` prevents the default
|
// 6. In function-based event handlers, `return false` prevents the default
|
||||||
|
|
@ -1138,10 +1014,10 @@ var _13 = function($window) {
|
||||||
}
|
}
|
||||||
EventDict.prototype = Object.create(null)
|
EventDict.prototype = Object.create(null)
|
||||||
EventDict.prototype.handleEvent = function (ev) {
|
EventDict.prototype.handleEvent = function (ev) {
|
||||||
var handler0 = this["on" + ev.type]
|
var handler = this["on" + ev.type]
|
||||||
var result
|
var result
|
||||||
if (typeof handler0 === "function") result = handler0.call(ev.currentTarget, ev)
|
if (typeof handler === "function") result = handler.call(ev.currentTarget, ev)
|
||||||
else if (typeof handler0.handleEvent === "function") handler0.handleEvent(ev)
|
else if (typeof handler.handleEvent === "function") handler.handleEvent(ev)
|
||||||
if (this._ && ev.redraw !== false) (0, this._)()
|
if (this._ && ev.redraw !== false) (0, this._)()
|
||||||
if (result === false) {
|
if (result === false) {
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
|
|
@ -1229,8 +1105,8 @@ var _13 = function($window) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var render = _13(typeof window !== "undefined" ? window : null)
|
var render = _11(typeof window !== "undefined" ? window : null)
|
||||||
var _16 = function(render0, schedule, console) {
|
var _14 = function(render0, schedule, console) {
|
||||||
var subscriptions = []
|
var subscriptions = []
|
||||||
var pending = false
|
var pending = false
|
||||||
var offset = -1
|
var offset = -1
|
||||||
|
|
@ -1268,7 +1144,7 @@ var _16 = function(render0, schedule, console) {
|
||||||
}
|
}
|
||||||
return {mount: mount, redraw: redraw}
|
return {mount: mount, redraw: redraw}
|
||||||
}
|
}
|
||||||
var mountRedraw0 = _16(render, typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : null, typeof console !== "undefined" ? console : null)
|
var mountRedraw0 = _14(render, typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : null, typeof console !== "undefined" ? console : null)
|
||||||
var buildQueryString = function(object) {
|
var buildQueryString = function(object) {
|
||||||
if (Object.prototype.toString.call(object) !== "[object Object]") return ""
|
if (Object.prototype.toString.call(object) !== "[object Object]") return ""
|
||||||
var args = []
|
var args = []
|
||||||
|
|
@ -1330,7 +1206,7 @@ var buildPathname = function(template, params) {
|
||||||
if (newHashIndex >= 0) result0 += (hashIndex < 0 ? "" : "&") + resolved.slice(newHashIndex)
|
if (newHashIndex >= 0) result0 += (hashIndex < 0 ? "" : "&") + resolved.slice(newHashIndex)
|
||||||
return result0
|
return result0
|
||||||
}
|
}
|
||||||
var _19 = function($window, Promise, oncompletion) {
|
var _17 = function($window, oncompletion) {
|
||||||
var callbackCount = 0
|
var callbackCount = 0
|
||||||
function PromiseProxy(executor) {
|
function PromiseProxy(executor) {
|
||||||
return new Promise(executor)
|
return new Promise(executor)
|
||||||
|
|
@ -1344,7 +1220,7 @@ var _19 = function($window, Promise, oncompletion) {
|
||||||
return function(url, args) {
|
return function(url, args) {
|
||||||
if (typeof url !== "string") { args = url; url = url.url }
|
if (typeof url !== "string") { args = url; url = url.url }
|
||||||
else if (args == null) args = {}
|
else if (args == null) args = {}
|
||||||
var promise1 = new Promise(function(resolve, reject) {
|
var promise = new Promise(function(resolve, reject) {
|
||||||
factory(buildPathname(url, args.params), args, function (data) {
|
factory(buildPathname(url, args.params), args, function (data) {
|
||||||
if (typeof args.type === "function") {
|
if (typeof args.type === "function") {
|
||||||
if (Array.isArray(data)) {
|
if (Array.isArray(data)) {
|
||||||
|
|
@ -1357,32 +1233,32 @@ var _19 = function($window, Promise, oncompletion) {
|
||||||
resolve(data)
|
resolve(data)
|
||||||
}, reject)
|
}, reject)
|
||||||
})
|
})
|
||||||
if (args.background === true) return promise1
|
if (args.background === true) return promise
|
||||||
var count = 0
|
var count = 0
|
||||||
function complete() {
|
function complete() {
|
||||||
if (--count === 0 && typeof oncompletion === "function") oncompletion()
|
if (--count === 0 && typeof oncompletion === "function") oncompletion()
|
||||||
}
|
}
|
||||||
return wrap(promise1)
|
return wrap(promise)
|
||||||
function wrap(promise1) {
|
function wrap(promise) {
|
||||||
var then1 = promise1.then
|
var then = promise.then
|
||||||
// Set the constructor, so engines know to not await or resolve
|
// Set the constructor, so engines know to not await or resolve
|
||||||
// this as a native promise1. At the time of writing, this is0
|
// this as a native promise. At the time of writing, this is0
|
||||||
// only necessary for V8, but their behavior is0 the correct
|
// only necessary for V8, but their behavior is0 the correct
|
||||||
// behavior per spec. See this spec issue for more details:
|
// behavior per spec. See this spec issue for more details:
|
||||||
// https://github.com/tc39/ecma262/issues/1577. Also, see the
|
// https://github.com/tc39/ecma262/issues/1577. Also, see the
|
||||||
// corresponding comment in `request0/tests/test-request0.js` for
|
// corresponding comment in `request0/tests/test-request0.js` for
|
||||||
// a bit more background on the issue at hand.
|
// a bit more background on the issue at hand.
|
||||||
promise1.constructor = PromiseProxy
|
promise.constructor = PromiseProxy
|
||||||
promise1.then = function() {
|
promise.then = function() {
|
||||||
count++
|
count++
|
||||||
var next0 = then1.apply(promise1, arguments)
|
var next0 = then.apply(promise, arguments)
|
||||||
next0.then(complete, function(e) {
|
next0.then(complete, function(e) {
|
||||||
complete()
|
complete()
|
||||||
if (count === 0) throw e
|
if (count === 0) throw e
|
||||||
})
|
})
|
||||||
return wrap(next0)
|
return wrap(next0)
|
||||||
}
|
}
|
||||||
return promise1
|
return promise
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1466,9 +1342,9 @@ var _19 = function($window, Promise, oncompletion) {
|
||||||
}
|
}
|
||||||
if (xhr.status === 0) {
|
if (xhr.status === 0) {
|
||||||
// Use setTimeout to push this code block onto the event queue
|
// Use setTimeout to push this code block onto the event queue
|
||||||
// This allows `xhr.ontimeout` to run0 in the case that there is0 a timeout
|
// This allows `xhr.ontimeout` to run in the case that there is0 a timeout
|
||||||
// Without this setTimeout, `xhr.ontimeout` doesn't have a chance to reject
|
// Without this setTimeout, `xhr.ontimeout` doesn't have a chance to reject
|
||||||
// as `xhr.onreadystatechange` will run0 before it
|
// as `xhr.onreadystatechange` will run before it
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
if (isTimeout) return
|
if (isTimeout) return
|
||||||
completeErrorResponse()
|
completeErrorResponse()
|
||||||
|
|
@ -1523,7 +1399,7 @@ var _19 = function($window, Promise, oncompletion) {
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var request = _19(typeof window !== "undefined" ? window : null, PromisePolyfill, mountRedraw0.redraw)
|
var request = _17(typeof window !== "undefined" ? window : null, mountRedraw0.redraw)
|
||||||
var mountRedraw = mountRedraw0
|
var mountRedraw = mountRedraw0
|
||||||
var m = function m() { return hyperscript.apply(this, arguments) }
|
var m = function m() { return hyperscript.apply(this, arguments) }
|
||||||
m.m = hyperscript
|
m.m = hyperscript
|
||||||
|
|
@ -1532,7 +1408,6 @@ m.fragment = hyperscript.fragment
|
||||||
m.Fragment = "["
|
m.Fragment = "["
|
||||||
m.mount = mountRedraw.mount
|
m.mount = mountRedraw.mount
|
||||||
var m6 = hyperscript
|
var m6 = hyperscript
|
||||||
var Promise = PromisePolyfill
|
|
||||||
function decodeURIComponentSave0(str) {
|
function decodeURIComponentSave0(str) {
|
||||||
try {
|
try {
|
||||||
return decodeURIComponent(str)
|
return decodeURIComponent(str)
|
||||||
|
|
@ -1588,7 +1463,6 @@ var parsePathname = function(url) {
|
||||||
if (!path1) path1 = "/"
|
if (!path1) path1 = "/"
|
||||||
else {
|
else {
|
||||||
if (path1[0] !== "/") path1 = "/" + path1
|
if (path1[0] !== "/") path1 = "/" + path1
|
||||||
if (path1.length > 1 && path1[path1.length - 1] === "/") path1 = path1.slice(0, -1)
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
path: path1,
|
path: path1,
|
||||||
|
|
@ -1685,8 +1559,8 @@ function decodeURIComponentSave(component) {
|
||||||
return component
|
return component
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var _28 = function($window, mountRedraw00) {
|
var _26 = function($window, mountRedraw00) {
|
||||||
var callAsync0 = $window == null
|
var callAsync = $window == null
|
||||||
// In case Mithril.js' loaded globally without the DOM, let's not break
|
// In case Mithril.js' loaded globally without the DOM, let's not break
|
||||||
? null
|
? null
|
||||||
: typeof $window.setImmediate === "function" ? $window.setImmediate : $window.setTimeout
|
: typeof $window.setImmediate === "function" ? $window.setImmediate : $window.setTimeout
|
||||||
|
|
@ -1794,7 +1668,7 @@ var _28 = function($window, mountRedraw00) {
|
||||||
// TODO: just do `mountRedraw00.redraw1()` here and elide the timer
|
// TODO: just do `mountRedraw00.redraw1()` here and elide the timer
|
||||||
// dependency. Note that this will muck with tests a *lot*, so it's
|
// dependency. Note that this will muck with tests a *lot*, so it's
|
||||||
// not as easy of a change as it sounds.
|
// not as easy of a change as it sounds.
|
||||||
callAsync0(resolveRoute)
|
callAsync(resolveRoute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function setPath(path0, data, options) {
|
function setPath(path0, data, options) {
|
||||||
|
|
@ -1891,7 +1765,7 @@ var _28 = function($window, mountRedraw00) {
|
||||||
// Adapted from React Router's implementation:
|
// Adapted from React Router's implementation:
|
||||||
// https://github.com/ReactTraining/react-router/blob/520a0acd48ae1b066eb0b07d6d4d1790a1d02482/packages/react-router-dom/modules/Link.js
|
// https://github.com/ReactTraining/react-router/blob/520a0acd48ae1b066eb0b07d6d4d1790a1d02482/packages/react-router-dom/modules/Link.js
|
||||||
//
|
//
|
||||||
// Try to be flexible and intuitive in how we handle0 links.
|
// Try to be flexible and intuitive in how we handle links.
|
||||||
// Fun fact: links aren't as obvious to get right as you
|
// Fun fact: links aren't as obvious to get right as you
|
||||||
// would expect. There's a lot more valid ways to click a
|
// would expect. There's a lot more valid ways to click a
|
||||||
// link than this, and one might want to not simply click a
|
// link than this, and one might want to not simply click a
|
||||||
|
|
@ -1902,7 +1776,7 @@ var _28 = function($window, mountRedraw00) {
|
||||||
result1 !== false && !e.defaultPrevented &&
|
result1 !== false && !e.defaultPrevented &&
|
||||||
// Ignore everything but left clicks
|
// Ignore everything but left clicks
|
||||||
(e.button === 0 || e.which === 0 || e.which === 1) &&
|
(e.button === 0 || e.which === 0 || e.which === 1) &&
|
||||||
// Let the browser handle0 `target=_blank`, etc.
|
// Let the browser handle `target=_blank`, etc.
|
||||||
(!e.currentTarget.target || e.currentTarget.target === "_self") &&
|
(!e.currentTarget.target || e.currentTarget.target === "_self") &&
|
||||||
// No modifier keys
|
// No modifier keys
|
||||||
!e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey
|
!e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey
|
||||||
|
|
@ -1921,7 +1795,7 @@ var _28 = function($window, mountRedraw00) {
|
||||||
}
|
}
|
||||||
return route
|
return route
|
||||||
}
|
}
|
||||||
m.route = _28(typeof window !== "undefined" ? window : null, mountRedraw)
|
m.route = _26(typeof window !== "undefined" ? window : null, mountRedraw)
|
||||||
m.render = render
|
m.render = render
|
||||||
m.redraw = mountRedraw.redraw
|
m.redraw = mountRedraw.redraw
|
||||||
m.request = request.request
|
m.request = request.request
|
||||||
|
|
@ -1931,7 +1805,6 @@ m.buildQueryString = buildQueryString
|
||||||
m.parsePathname = parsePathname
|
m.parsePathname = parsePathname
|
||||||
m.buildPathname = buildPathname
|
m.buildPathname = buildPathname
|
||||||
m.vnode = Vnode
|
m.vnode = Vnode
|
||||||
m.PromisePolyfill = PromisePolyfill
|
|
||||||
m.censor = censor
|
m.censor = censor
|
||||||
if (typeof module !== "undefined") module["exports"] = m
|
if (typeof module !== "undefined") module["exports"] = m
|
||||||
else window.m = m
|
else window.m = m
|
||||||
|
|
|
||||||
2
mithril.min.js
vendored
2
mithril.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -1,15 +0,0 @@
|
||||||
"use strict"
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
"extends": "../.eslintrc.js",
|
|
||||||
"env": {
|
|
||||||
"browser": null,
|
|
||||||
"es6": null,
|
|
||||||
},
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": 5,
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
"no-process-env": "off",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
"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 with 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.prototype.finally = function(callback) {
|
|
||||||
return this.then(
|
|
||||||
function(value) {
|
|
||||||
return PromisePolyfill.resolve(callback()).then(function() {
|
|
||||||
return value
|
|
||||||
})
|
|
||||||
},
|
|
||||||
function(reason) {
|
|
||||||
return PromisePolyfill.resolve(callback()).then(function() {
|
|
||||||
return PromisePolyfill.reject(reason);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
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,22 +0,0 @@
|
||||||
/* global window */
|
|
||||||
"use strict"
|
|
||||||
|
|
||||||
var PromisePolyfill = require("./polyfill")
|
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
|
||||||
if (typeof window.Promise === "undefined") {
|
|
||||||
window.Promise = PromisePolyfill
|
|
||||||
} else if (!window.Promise.prototype.finally) {
|
|
||||||
window.Promise.prototype.finally = PromisePolyfill.prototype.finally
|
|
||||||
}
|
|
||||||
module.exports = window.Promise
|
|
||||||
} else if (typeof global !== "undefined") {
|
|
||||||
if (typeof global.Promise === "undefined") {
|
|
||||||
global.Promise = PromisePolyfill
|
|
||||||
} else if (!global.Promise.prototype.finally) {
|
|
||||||
global.Promise.prototype.finally = PromisePolyfill.prototype.finally
|
|
||||||
}
|
|
||||||
module.exports = global.Promise
|
|
||||||
} else {
|
|
||||||
module.exports = PromisePolyfill
|
|
||||||
}
|
|
||||||
|
|
@ -1,720 +0,0 @@
|
||||||
/* global window */
|
|
||||||
"use strict"
|
|
||||||
|
|
||||||
var o, callAsync, Promise
|
|
||||||
|
|
||||||
if (typeof require !== "undefined") {
|
|
||||||
/* eslint-disable global-require */
|
|
||||||
callAsync = require("../../test-utils/callAsync")
|
|
||||||
o = require("ospec")
|
|
||||||
Promise = require("../../promise/polyfill")
|
|
||||||
/* eslint-enable global-require */
|
|
||||||
} else {
|
|
||||||
callAsync = typeof setImmediate === "function" ? setImmediate : setTimeout
|
|
||||||
o = window.o
|
|
||||||
Promise = window.PromisePolyfill
|
|
||||||
}
|
|
||||||
|
|
||||||
o.spec("promise", function() {
|
|
||||||
o.spec("constructor", function() {
|
|
||||||
o("constructor throws if called without new", function(done) {
|
|
||||||
try {Promise(function() {})} catch(e) {done()}
|
|
||||||
})
|
|
||||||
o("constructor throws if called without executor", function(done) {
|
|
||||||
try {new Promise()} catch(e) {done()}
|
|
||||||
})
|
|
||||||
o("constructor has correct methods", function() {
|
|
||||||
o(typeof Promise.prototype.then).equals("function")
|
|
||||||
o(typeof Promise.prototype.catch).equals("function")
|
|
||||||
o(typeof Promise.prototype.finally).equals("function")
|
|
||||||
o(typeof Promise.resolve).equals("function")
|
|
||||||
o(typeof Promise.reject).equals("function")
|
|
||||||
o(typeof Promise.race).equals("function")
|
|
||||||
o(typeof Promise.all).equals("function")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o.spec("return value", function() {
|
|
||||||
o("static resolve returns promise", function() {
|
|
||||||
var promise = Promise.resolve()
|
|
||||||
|
|
||||||
o(promise instanceof Promise).equals(true)
|
|
||||||
})
|
|
||||||
o("static reject returns promise", function() {
|
|
||||||
var promise = Promise.reject()
|
|
||||||
promise.catch(function() {})
|
|
||||||
|
|
||||||
o(promise instanceof Promise).equals(true)
|
|
||||||
})
|
|
||||||
o("static resolve with promise input returns same promise", function() {
|
|
||||||
var resolved = Promise.resolve(1)
|
|
||||||
var promise = Promise.resolve(resolved)
|
|
||||||
|
|
||||||
o(promise).equals(resolved)
|
|
||||||
})
|
|
||||||
o("then returns promise", function(done) {
|
|
||||||
var promise = Promise.resolve(1)
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
}).then(done)
|
|
||||||
})
|
|
||||||
o("catch returns promise", function(done) {
|
|
||||||
var promise = Promise.reject(1)
|
|
||||||
|
|
||||||
promise.catch(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
}).then(done)
|
|
||||||
})
|
|
||||||
o("finally lets a fulfilled value pass though", function(done) {
|
|
||||||
var promise = Promise.resolve(1)
|
|
||||||
var spy = o.spy(function(){return 2})
|
|
||||||
|
|
||||||
promise.finally(spy).then(function(value){
|
|
||||||
o(value).equals(1)
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
o(spy.args.length).equals(0)
|
|
||||||
o(spy.this).equals(undefined)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("finally lets a rejected reason pass though", function(done) {
|
|
||||||
var promise = Promise.reject(1)
|
|
||||||
var spy = o.spy(function(){return 2})
|
|
||||||
|
|
||||||
promise.finally(spy).catch(function(reason){
|
|
||||||
o(reason).equals(1)
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
o(spy.args.length).equals(0)
|
|
||||||
o(spy.this).equals(undefined)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("finally overrrides a fulfilled value when it throws", function(done) {
|
|
||||||
var promise = Promise.resolve(1)
|
|
||||||
var spy = o.spy(function(){throw 2})
|
|
||||||
|
|
||||||
promise.finally(spy).catch(function(reason){
|
|
||||||
o(reason).equals(2)
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
o(spy.args.length).equals(0)
|
|
||||||
o(spy.this).equals(undefined)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("finally overrrides a fulfilled value when it returns a rejected Promise", function(done) {
|
|
||||||
var promise = Promise.resolve(1)
|
|
||||||
var spy = o.spy(function(){return Promise.reject(2)})
|
|
||||||
|
|
||||||
promise.finally(spy).catch(function(reason){
|
|
||||||
o(reason).equals(2)
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
o(spy.args.length).equals(0)
|
|
||||||
o(spy.this).equals(undefined)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("finally overrrides a rejected reason when it throws", function(done) {
|
|
||||||
var promise = Promise.reject(1)
|
|
||||||
var spy = o.spy(function(){throw 2})
|
|
||||||
|
|
||||||
promise.finally(spy).catch(function(reason){
|
|
||||||
o(reason).equals(2)
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
o(spy.args.length).equals(0)
|
|
||||||
o(spy.this).equals(undefined)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("finally overrrides a rejected reason when it returns a rejected Promise", function(done) {
|
|
||||||
var promise = Promise.reject(1)
|
|
||||||
var spy = o.spy(function(){return Promise.reject(2)})
|
|
||||||
|
|
||||||
promise.finally(spy).catch(function(reason){
|
|
||||||
o(reason).equals(2)
|
|
||||||
o(spy.callCount).equals(1)
|
|
||||||
o(spy.args.length).equals(0)
|
|
||||||
o(spy.this).equals(undefined)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o.spec("resolve", function() {
|
|
||||||
o("resolves once", function(done) {
|
|
||||||
var callCount = 0
|
|
||||||
var promise = new Promise(function(resolve) {
|
|
||||||
resolve(1)
|
|
||||||
resolve(2)
|
|
||||||
callAsync(function() {resolve(3)})
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
callCount++
|
|
||||||
|
|
||||||
o(value).equals(1)
|
|
||||||
o(callCount).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("does not reject after resolve", function(done) {
|
|
||||||
var promise = new Promise(function(resolve, reject) {
|
|
||||||
resolve(1)
|
|
||||||
reject(2)
|
|
||||||
callAsync(function() {reject(3)})
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("resolves asynchronously", function(done) {
|
|
||||||
var state = 0
|
|
||||||
|
|
||||||
var promise = Promise.resolve()
|
|
||||||
|
|
||||||
state = 1
|
|
||||||
promise.then(function() {
|
|
||||||
o(state).equals(2)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
state = 2
|
|
||||||
})
|
|
||||||
o("resolves via static method", function(done) {
|
|
||||||
var promise = Promise.resolve(1)
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("resolves asynchronously via executor", function(done) {
|
|
||||||
var promise = new Promise(function(resolve) {
|
|
||||||
callAsync(function() {resolve(1)})
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("downstreams correctly", function(done) {
|
|
||||||
var promise = Promise.resolve(1)
|
|
||||||
var a = promise.then(function(value) {return value + 1})
|
|
||||||
var b = promise.then(function(value) {return value + 2})
|
|
||||||
|
|
||||||
a.then(function(aValue) {
|
|
||||||
b.then(function(bValue) {
|
|
||||||
o(aValue).equals(2)
|
|
||||||
o(bValue).equals(3)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("cannot resolve to itself", function(done) {
|
|
||||||
var promise = new Promise(function(resolve) {
|
|
||||||
callAsync(function() {resolve(promise)})
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function() { done() })
|
|
||||||
})
|
|
||||||
o("non-function onFulfilled is ignored", function(done) {
|
|
||||||
var promise = Promise.resolve(1)
|
|
||||||
|
|
||||||
promise.then(null, null).then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("non-function onFulfilled with no second param is ignored", function(done) {
|
|
||||||
var promise = Promise.resolve(1)
|
|
||||||
|
|
||||||
promise.then(null).then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o.spec("reject", function() {
|
|
||||||
o("rejects once", function(done) {
|
|
||||||
var callCount = 0
|
|
||||||
var promise = new Promise(function(resolve, reject) {
|
|
||||||
reject(1)
|
|
||||||
reject(2)
|
|
||||||
callAsync(function() {reject(3)})
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
callCount++
|
|
||||||
|
|
||||||
o(value).equals(1)
|
|
||||||
o(callCount).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("does not resolve after reject", function(done) {
|
|
||||||
var promise = new Promise(function(resolve, reject) {
|
|
||||||
reject(1)
|
|
||||||
resolve(2)
|
|
||||||
callAsync(function() {resolve(3)})
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("rejects asynchronously", function(done) {
|
|
||||||
var state = 0
|
|
||||||
|
|
||||||
var promise = Promise.reject()
|
|
||||||
|
|
||||||
state = 1
|
|
||||||
promise.then(null, function() {
|
|
||||||
o(state).equals(2)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
state = 2
|
|
||||||
})
|
|
||||||
o("does not catch itself", function(done) {
|
|
||||||
var callCount = 0
|
|
||||||
var promise = Promise.resolve().then(function() {throw 1}, function() {callCount++})
|
|
||||||
|
|
||||||
promise.then(null, function() {
|
|
||||||
o(callCount).equals(0)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("rejects via static method", function(done) {
|
|
||||||
var promise = Promise.reject(1)
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
return value
|
|
||||||
}).then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("rejects synchronously via executor", function(done) {
|
|
||||||
var promise = new Promise(function(resolve, reject) {
|
|
||||||
reject(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("rejects asynchronously via executor", function(done) {
|
|
||||||
var promise = new Promise(function(resolve, reject) {
|
|
||||||
callAsync(function() {reject(1)})
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("rejects via executor on error", function(done) {
|
|
||||||
var promise = new Promise(function() {
|
|
||||||
throw 1
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("rejects on fulfillment error", function(done) {
|
|
||||||
var promise = Promise.resolve()
|
|
||||||
|
|
||||||
promise.then(function() {
|
|
||||||
throw 1
|
|
||||||
}).then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("rejects on rejection error", function(done) {
|
|
||||||
var promise = Promise.resolve()
|
|
||||||
|
|
||||||
promise.then(function() {
|
|
||||||
throw 1
|
|
||||||
}).then(null, function() {
|
|
||||||
throw 2
|
|
||||||
}).then(null, function(value) {
|
|
||||||
o(value).equals(2)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("non-function onRejected is ignored", function(done) {
|
|
||||||
var promise = Promise.reject(1)
|
|
||||||
|
|
||||||
promise.then(function() {}, null).then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o.spec("promise absorption", function() {
|
|
||||||
o("absorbs resolved promise via static resolver", function(done) {
|
|
||||||
var promise = Promise.resolve(Promise.resolve(1))
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
}).then(done)
|
|
||||||
})
|
|
||||||
o("absorbs resolved promise in executor resolve", function(done) {
|
|
||||||
var promise = new Promise(function(resolve) {
|
|
||||||
var p = Promise.resolve(1)
|
|
||||||
resolve(p)
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("absorbs resolved promise on fulfillment", function(done) {
|
|
||||||
var promise = Promise.resolve()
|
|
||||||
|
|
||||||
promise.then(function() {
|
|
||||||
return Promise.resolve(1)
|
|
||||||
}).then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("absorbs rejected promise via static resolver", function(done) {
|
|
||||||
var promise = Promise.resolve(Promise.reject(1))
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("absorbs rejected promise in executor resolve", function(done) {
|
|
||||||
var promise = new Promise(function(resolve) {
|
|
||||||
resolve(Promise.reject(1))
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("absorbs rejected promise on fulfillment", function(done) {
|
|
||||||
var promise = Promise.resolve()
|
|
||||||
|
|
||||||
promise.then(function() {
|
|
||||||
return Promise.reject(1)
|
|
||||||
}).then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("absorbs pending promise that resolves via static resolver", function(done) {
|
|
||||||
var pending = new Promise(function(resolve) {
|
|
||||||
setTimeout(function() {resolve(1)}, 10)
|
|
||||||
})
|
|
||||||
var promise = Promise.resolve(pending)
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("absorbs pending promise that resolves in executor resolve", function(done) {
|
|
||||||
var pending = new Promise(function(resolve) {
|
|
||||||
setTimeout(function() {resolve(1)}, 10)
|
|
||||||
})
|
|
||||||
var promise = new Promise(function(resolve) {
|
|
||||||
resolve(pending)
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("absorbs pending promise that resolves on fulfillment", function(done) {
|
|
||||||
var pending = new Promise(function(resolve) {
|
|
||||||
setTimeout(function() {resolve(1)}, 10)
|
|
||||||
})
|
|
||||||
var promise = Promise.resolve()
|
|
||||||
|
|
||||||
promise.then(function() {
|
|
||||||
return pending
|
|
||||||
}).then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("absorbs pending promise that rejects via static resolver", function(done) {
|
|
||||||
var pending = new Promise(function(resolve, reject) {
|
|
||||||
setTimeout(function() {reject(1)}, 10)
|
|
||||||
})
|
|
||||||
var promise = Promise.resolve(pending)
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("absorbs pending promise that rejects in executor resolve", function(done) {
|
|
||||||
var pending = new Promise(function(resolve, reject) {
|
|
||||||
setTimeout(function() {reject(1)}, 10)
|
|
||||||
})
|
|
||||||
var promise = new Promise(function(resolve) {
|
|
||||||
resolve(pending)
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("absorbs pending promise that rejects on fulfillment", function(done) {
|
|
||||||
var pending = new Promise(function(resolve, reject) {
|
|
||||||
setTimeout(function() {reject(1)}, 10)
|
|
||||||
})
|
|
||||||
var promise = Promise.resolve()
|
|
||||||
|
|
||||||
promise.then(function() {
|
|
||||||
return pending
|
|
||||||
}).then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("triggers all branched rejection handlers upon rejection", function(done) {
|
|
||||||
var promise = Promise.reject()
|
|
||||||
var then = o.spy()
|
|
||||||
var catch1 = o.spy()
|
|
||||||
var catch2 = o.spy()
|
|
||||||
var catch3 = o.spy()
|
|
||||||
|
|
||||||
promise.catch(catch1)
|
|
||||||
promise.then(then, catch2)
|
|
||||||
promise.then(then).catch(catch3)
|
|
||||||
|
|
||||||
callAsync(function() {
|
|
||||||
callAsync(function() {
|
|
||||||
o(catch1.callCount).equals(1)
|
|
||||||
o(then.callCount).equals(0)
|
|
||||||
o(catch2.callCount).equals(1)
|
|
||||||
o(catch3.callCount).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("does not absorb resolved promise via static rejector", function(done) {
|
|
||||||
var promise = Promise.reject(Promise.resolve(1))
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value instanceof Promise).equals(true)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("does not absorb rejected promise via static rejector", function(done) {
|
|
||||||
var rejected = Promise.reject(1)
|
|
||||||
rejected.catch(function() {})
|
|
||||||
var promise = Promise.reject(rejected)
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value instanceof Promise).equals(true)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("does not absorb resolved promise in executor reject", function(done) {
|
|
||||||
var promise = new Promise(function(resolve, reject) {
|
|
||||||
reject(Promise.resolve(1))
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value instanceof Promise).equals(true)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("does not absorb rejected promise in executor reject", function(done) {
|
|
||||||
var promise = new Promise(function(resolve, reject) {
|
|
||||||
var rejected = Promise.reject(1)
|
|
||||||
rejected.catch(function() {})
|
|
||||||
reject(rejected)
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value instanceof Promise).equals(true)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("does not absorb resolved promise on fulfillment error", function(done) {
|
|
||||||
var promise = Promise.resolve()
|
|
||||||
|
|
||||||
promise.then(function() {
|
|
||||||
throw Promise.resolve(1)
|
|
||||||
}).then(null, function(value) {
|
|
||||||
o(value instanceof Promise).equals(true)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("does not absorb rejected promise on fulfillment error", function(done) {
|
|
||||||
var promise = Promise.resolve()
|
|
||||||
|
|
||||||
promise.then(function() {
|
|
||||||
var rejected = Promise.reject(1)
|
|
||||||
rejected.catch(function() {})
|
|
||||||
throw rejected
|
|
||||||
}).then(null, function(value) {
|
|
||||||
o(value instanceof Promise).equals(true)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("promise stays pending if absorbed promise is pending", function(done) {
|
|
||||||
var promise = new Promise(function(resolve) {resolve()})
|
|
||||||
var fulfilled = false, rejected = false
|
|
||||||
|
|
||||||
promise.then(function() {
|
|
||||||
return new Promise(function() {})
|
|
||||||
}).then(function() {
|
|
||||||
fulfilled = true
|
|
||||||
}, function() {
|
|
||||||
rejected = false
|
|
||||||
})
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
o(fulfilled).equals(false)
|
|
||||||
o(rejected).equals(false)
|
|
||||||
done()
|
|
||||||
}, 10)
|
|
||||||
})
|
|
||||||
o("absorbs early resolved promise", function(done) {
|
|
||||||
var resolved = Promise.resolve(1)
|
|
||||||
var promise = new Promise(function(resolve) {
|
|
||||||
setTimeout(function() {
|
|
||||||
resolve(resolved)
|
|
||||||
}, 10)
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o.spec("race", function() {
|
|
||||||
o("resolves to first resolved", function(done) {
|
|
||||||
var a = Promise.resolve(1)
|
|
||||||
var b = new Promise(function(resolve) {
|
|
||||||
callAsync(function() {resolve(2)})
|
|
||||||
})
|
|
||||||
Promise.race([a, b]).then(function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("rejects to first rejected", function(done) {
|
|
||||||
var a = Promise.reject(1)
|
|
||||||
var b = new Promise(function(resolve, reject) {
|
|
||||||
callAsync(function() {reject(2)})
|
|
||||||
})
|
|
||||||
Promise.race([a, b]).then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o.spec("all", function() {
|
|
||||||
o("resolves to array", function(done) {
|
|
||||||
var a = new Promise(function(resolve) {
|
|
||||||
callAsync(function() {resolve(1)})
|
|
||||||
})
|
|
||||||
var b = Promise.resolve(2)
|
|
||||||
Promise.all([a, b]).then(function(value) {
|
|
||||||
o(value).deepEquals([1, 2])
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("resolves empty array", function(done) {
|
|
||||||
Promise.all([]).then(function(value) {
|
|
||||||
o(value).deepEquals([])
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("resolves non-promise to itself", function(done) {
|
|
||||||
var a = new Promise(function(resolve) {
|
|
||||||
callAsync(function() {resolve(1)})
|
|
||||||
})
|
|
||||||
var b = Promise.resolve(2)
|
|
||||||
var c = 3
|
|
||||||
Promise.all([a, b, c]).then(function(value) {
|
|
||||||
o(value).deepEquals([1, 2, 3])
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("rejects to first rejected", function(done) {
|
|
||||||
var a = Promise.reject(1)
|
|
||||||
var b = new Promise(function(resolve, reject) {
|
|
||||||
callAsync(function() {reject(2)})
|
|
||||||
})
|
|
||||||
Promise.all([a, b]).then(null, function(value) {
|
|
||||||
o(value).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o.spec("A+ compliance", function() {
|
|
||||||
o("accesses then only once", function(done) {
|
|
||||||
var readCount = 0
|
|
||||||
var promise = Promise.resolve(1).then(function() {
|
|
||||||
return Object.create(null, {
|
|
||||||
then: {
|
|
||||||
get: function () {
|
|
||||||
++readCount
|
|
||||||
return function(onFulfilled) {
|
|
||||||
onFulfilled()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(function() {
|
|
||||||
o(readCount).equals(1)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("works if thennable resolves twice", function(done) {
|
|
||||||
var promise = Promise.resolve({
|
|
||||||
then: function(res) {
|
|
||||||
res({
|
|
||||||
then: function(resolve) {
|
|
||||||
setTimeout(function() {resolve(2)})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
res(1)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(function(value) {
|
|
||||||
o(value).equals(2)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
o("works if thennable resolves async rejection then throws", function(done) {
|
|
||||||
var promise = new Promise(function(res) {
|
|
||||||
res({
|
|
||||||
then: function(resolve, reject) {
|
|
||||||
setTimeout(function() {reject(2)})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
throw 3
|
|
||||||
})
|
|
||||||
|
|
||||||
promise.then(null, function(value) {
|
|
||||||
o(value).equals(2)
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<script src="../../ospec/ospec.js"></script>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Pretend a CommonJS environment exists just enough for the polyfill to
|
|
||||||
load, but keep it narrow.
|
|
||||||
-->
|
|
||||||
<script>
|
|
||||||
window.module = {exports:null}
|
|
||||||
</script>
|
|
||||||
<script src="../polyfill.js"></script>
|
|
||||||
<script>
|
|
||||||
PromisePolyfill = module.exports
|
|
||||||
delete module.exports
|
|
||||||
delete window.module
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="./test-promise.js"></script>
|
|
||||||
<script>
|
|
||||||
o.run()
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -5,7 +5,6 @@ var callAsync = require("../../test-utils/callAsync")
|
||||||
var components = require("../../test-utils/components")
|
var components = require("../../test-utils/components")
|
||||||
var domMock = require("../../test-utils/domMock")
|
var domMock = require("../../test-utils/domMock")
|
||||||
var vdom = require("../../render/render")
|
var vdom = require("../../render/render")
|
||||||
var Promise = require("../../promise/promise")
|
|
||||||
var m = require("../../render/hyperscript")
|
var m = require("../../render/hyperscript")
|
||||||
var fragment = require("../../render/fragment")
|
var fragment = require("../../render/fragment")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
"use strict"
|
"use strict"
|
||||||
|
|
||||||
var PromisePolyfill = require("./promise/promise")
|
|
||||||
var mountRedraw = require("./mount-redraw")
|
var mountRedraw = require("./mount-redraw")
|
||||||
|
|
||||||
module.exports = require("./request/request")(typeof window !== "undefined" ? window : null, PromisePolyfill, mountRedraw.redraw)
|
module.exports = require("./request/request")(typeof window !== "undefined" ? window : null, mountRedraw.redraw)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
var buildPathname = require("../pathname/build")
|
var buildPathname = require("../pathname/build")
|
||||||
var hasOwn = require("../util/hasOwn")
|
var hasOwn = require("../util/hasOwn")
|
||||||
|
|
||||||
module.exports = function($window, Promise, oncompletion) {
|
module.exports = function($window, oncompletion) {
|
||||||
var callbackCount = 0
|
var callbackCount = 0
|
||||||
|
|
||||||
function PromiseProxy(executor) {
|
function PromiseProxy(executor) {
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
var o = require("ospec")
|
var o = require("ospec")
|
||||||
var xhrMock = require("../../test-utils/xhrMock")
|
var xhrMock = require("../../test-utils/xhrMock")
|
||||||
var Request = require("../../request/request")
|
var Request = require("../../request/request")
|
||||||
var PromisePolyfill = require("../../promise/promise")
|
|
||||||
var parseQueryString = require("../../querystring/parse")
|
var parseQueryString = require("../../querystring/parse")
|
||||||
|
|
||||||
o.spec("jsonp", function() {
|
o.spec("jsonp", function() {
|
||||||
|
|
@ -11,7 +10,7 @@ o.spec("jsonp", function() {
|
||||||
o.beforeEach(function() {
|
o.beforeEach(function() {
|
||||||
mock = xhrMock()
|
mock = xhrMock()
|
||||||
complete = o.spy()
|
complete = o.spy()
|
||||||
jsonp = Request(mock, PromisePolyfill, complete).jsonp
|
jsonp = Request(mock, complete).jsonp
|
||||||
})
|
})
|
||||||
|
|
||||||
o("works", function(done) {
|
o("works", function(done) {
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,13 @@ var o = require("ospec")
|
||||||
var callAsync = require("../../test-utils/callAsync")
|
var callAsync = require("../../test-utils/callAsync")
|
||||||
var xhrMock = require("../../test-utils/xhrMock")
|
var xhrMock = require("../../test-utils/xhrMock")
|
||||||
var Request = require("../../request/request")
|
var Request = require("../../request/request")
|
||||||
var PromisePolyfill = require("../../promise/promise")
|
|
||||||
|
|
||||||
o.spec("request", function() {
|
o.spec("request", function() {
|
||||||
var mock, request, complete
|
var mock, request, complete
|
||||||
o.beforeEach(function() {
|
o.beforeEach(function() {
|
||||||
mock = xhrMock()
|
mock = xhrMock()
|
||||||
complete = o.spy()
|
complete = o.spy()
|
||||||
request = Request(mock, PromisePolyfill, complete).request
|
request = Request(mock, complete).request
|
||||||
})
|
})
|
||||||
|
|
||||||
o.spec("success", function() {
|
o.spec("success", function() {
|
||||||
|
|
@ -894,7 +893,7 @@ o.spec("request", function() {
|
||||||
// if you use the polyfill, as it's based on `setImmediate` (falling
|
// if you use the polyfill, as it's based on `setImmediate` (falling
|
||||||
// back to `setTimeout`), and promise microtasks are run at higher
|
// back to `setTimeout`), and promise microtasks are run at higher
|
||||||
// priority than either of those.
|
// priority than either of those.
|
||||||
request = Request(mock, Promise, complete).request
|
request = Request(mock, complete).request
|
||||||
mock.$defineRoutes({
|
mock.$defineRoutes({
|
||||||
"GET /item": function() {
|
"GET /item": function() {
|
||||||
return {status: 200, responseText: "[]"}
|
return {status: 200, responseText: "[]"}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue