scheduler-polyfill
Advanced tools
Comparing version 1.2.1 to 1.3.0-beta.0
@@ -1,2 +0,2 @@ | ||
!function(){var e=["user-blocking","user-visible","background"];class t{constructor(){this.channel_=new MessageChannel,this.sendPort_=this.channel_.port2,this.messages_={},this.nextMessageHandle_=1,this.channel_.port1.onmessage=e=>this.onMessageReceived_(e)}queueCallback(e){var t=this.nextMessageHandle_++;return this.messages_[t]=e,this.sendPort_.postMessage(t),t}cancelCallback(e){delete this.messages_[e]}onMessageReceived_(e){var t=e.data;if(t in this.messages_){var i=this.messages_[t];delete this.messages_[t],i()}}}function i(){return i.instance_||(i.instance_=new t),i.instance_}class s{constructor(e,t,i){void 0===i&&(i=0),this.callback_=e,this.callbackType_=null,this.handle_=null,this.canceled_=!1,this.schedule_(t,i)}isIdleCallback(){return 0===this.callbackType_}isMessageChannelCallback(){return 2===this.callbackType_}cancel(){if(!this.canceled_)switch(this.canceled_=!0,this.callbackType_){case 0:cancelIdleCallback(this.handle_);break;case 1:clearTimeout(this.handle_);break;case 2:i().cancelCallback(this.handle_);break;default:throw new TypeError("Unknown CallbackType")}}schedule_(t,s){if(s&&s>0)return this.callbackType_=1,void(this.handle_=setTimeout(()=>{this.runCallback_()},s));if(!e.includes(t))throw new TypeError("Invalid task priority : "+t);return"background"===t&&"function"==typeof requestIdleCallback?(this.callbackType_=0,void(this.handle_=requestIdleCallback(()=>{this.runCallback_()}))):"function"==typeof MessageChannel?(this.callbackType_=2,void(this.handle_=i().queueCallback(()=>{this.runCallback_()}))):(this.callbackType_=1,void(this.handle_=setTimeout(()=>{this.runCallback_()})))}runCallback_(){this.canceled_||this.callback_()}}var r=0;class a{constructor(){this.head_=null,this.tail_=null}isEmpty(){return null==this.head_}push(e){if("object"!=typeof e)throw new TypeError("Task must be an Object");e.tq_sequence_=r++,this.isEmpty()?(e.tq_prev_=null,this.head_=e):(e.tq_prev_=this.tail_,this.tail_.tq_next_=e),e.tq_next_=null,this.tail_=e}takeNextTask(){if(this.isEmpty())return null;var e=this.head_;return this.remove_(e),e}merge(e,t){if("function"!=typeof t)throw new TypeError("Must provide a selector function.");if(null==e)throw new Error("sourceQueue cannot be null");for(var i=this.head_,s=null,r=e.head_;r;){var a=r;if(r=r.tq_next_,t(a)){for(e.remove_(a);i&&i.tq_sequence_<a.tq_sequence_;)s=i,i=i.tq_next_;this.insert_(a,s),s=a}}}toArray(){for(var e=this.head_,t=[];null!==e;)t.push(e),e=e.tq_next_;return t}insert_(e,t){if(t!=this.tail_){var i=t?t.tq_next_:this.head_;e.tq_next_=i,i.tq_prev_=e,e.tq_prev_=t,null!=t?t.tq_next_=e:this.head_=e}else this.push(e)}remove_(e){if(null==e)throw new Error("Expected task to be non-null");e===this.head_&&(this.head_=e.tq_next_),e===this.tail_&&(this.tail_=this.tail_.tq_prev_),e.tq_next_&&(e.tq_next_.tq_prev_=e.tq_prev_),e.tq_prev_&&(e.tq_prev_.tq_next_=e.tq_next_)}}class n extends Event{constructor(t,i){if(!i||!e.includes(i.previousPriority))throw new TypeError("Invalid task priority: '"+i.previousPriority+"'");super(t),this.previousPriority=i.previousPriority}}class l extends AbortController{constructor(t){if(void 0===t&&(t={}),super(),null==t&&(t={}),"object"!=typeof t)throw new TypeError("'init' is not an object");var i,s,r=void 0===t.priority?"user-visible":t.priority;if(!e.includes(r))throw new TypeError("Invalid task priority: '"+r+"'");this.priority_=r,this.isPriorityChanging_=!1,s=(i=this).signal,Object.defineProperties(s,{priority:{get:function(){return i.priority_},enumerable:!0},onprioritychange:{value:null,writable:!0,enumerable:!0}}),s.addEventListener("prioritychange",e=>{s.onprioritychange&&s.onprioritychange(e)})}setPriority(t){if(!e.includes(t))throw new TypeError("Invalid task priority: "+t);if(this.isPriorityChanging_)throw new DOMException("","NotAllowedError");if(this.signal.priority!==t){this.isPriorityChanging_=!0;var i=this.priority_;this.priority_=t;var s=new n("prioritychange",{previousPriority:i});this.signal.dispatchEvent(s),this.isPriorityChanging_=!1}}}void 0===self.scheduler&&(self.scheduler=new class{constructor(){this.queues_={},e.forEach(e=>{this.queues_[e]=new a}),this.pendingHostCallback_=null,this.signals_=new WeakMap}postTask(t,i){if(void 0!==(i=Object.assign({},i)).signal){if(null===i.signal||!("aborted"in i.signal)||"function"!=typeof i.signal.addEventListener)return Promise.reject(new TypeError("'signal' is not a valid 'AbortSignal'"));if(i.signal&&i.signal.priority&&!e.includes(i.signal.priority))return Promise.reject(new TypeError("Invalid task priority: '"+i.signal.priority+"'"))}if(void 0!==i.priority&&(null===i.priority||!e.includes(i.priority)))return Promise.reject(new TypeError("Invalid task priority: '"+i.priority+"'"));if(void 0===i.delay&&(i.delay=0),i.delay=Number(i.delay),i.delay<0)return Promise.reject(new TypeError("'delay' must be a positive number."));var s={callback:t,options:i,resolve:null,reject:null,hostCallback:null,abortCallback:null,onTaskCompleted:function(){this.options.signal&&this.abortCallback&&(this.options.signal.removeEventListener("abort",this.abortCallback),this.abortCallback=null)},onTaskAborted:function(){this.hostCallback&&(this.hostCallback.cancel(),this.hostCallback=null),this.options.signal.removeEventListener("abort",this.abortCallback),this.abortCallback=null,this.reject(this.options.signal.reason)},isAborted:function(){return this.options.signal&&this.options.signal.aborted}},r=new Promise((e,t)=>{s.resolve=e,s.reject=t});return this.schedule_(s),r}schedule_(e){var t=e.options.signal;if(t){if(t.aborted)return void e.reject(t.reason);e.abortCallback=()=>{e.onTaskAborted()},t.addEventListener("abort",e.abortCallback)}e.options.delay>0?e.hostCallback=new s(()=>{e.hostCallback=null,this.onTaskDelayExpired_(e)},null,e.options.delay):(this.pushTask_(e),this.scheduleHostCallbackIfNeeded_())}onTaskDelayExpired_(e){this.pushTask_(e),this.pendingHostCallback_&&(this.pendingHostCallback_.cancel(),this.pendingHostCallback_=null),this.schedulerEntryCallback_()}onPriorityChange_(e){var t=this.signals_.get(e);if(void 0===t)throw new Error("Attempting to change priority on an unregistered signal");t!==e.priority&&(this.queues_[e.priority].merge(this.queues_[t],t=>t.options.signal===e),this.signals_.set(e,e.priority))}schedulerEntryCallback_(){this.pendingHostCallback_=null,this.runNextTask_(),this.scheduleHostCallbackIfNeeded_()}scheduleHostCallbackIfNeeded_(){var e=this.nextTaskPriority_();null!=e&&("background"!==e&&this.pendingHostCallback_&&this.pendingHostCallback_.isIdleCallback()&&(this.pendingHostCallback_.cancel(),this.pendingHostCallback_=null),this.pendingHostCallback_||(this.pendingHostCallback_=new s(()=>{this.schedulerEntryCallback_()},e,0)))}pushTask_(t){var i;if(!e.includes(i=t.options.priority?t.options.priority:t.options.signal&&t.options.signal.priority?t.options.signal.priority:"user-visible"))throw new TypeError("Invalid task priority: "+i);if(t.options.signal&&t.options.signal.priority){var s=t.options.signal;this.signals_.has(s)||(s.addEventListener("prioritychange",()=>{this.onPriorityChange_(s)}),this.signals_.set(s,s.priority))}this.queues_[i].push(t)}runNextTask_(){var e=null;do{var t=this.nextTaskPriority_();if(null==t)return;e=this.queues_[t].takeNextTask()}while(e.isAborted());try{var i=e.callback();e.resolve(i)}catch(t){e.reject(t)}finally{e.onTaskCompleted()}}nextTaskPriority_(){for(var t=0;t<e.length;t++){var i=e[t];if(!this.queues_[i].isEmpty())return i}return null}},self.TaskController=l,self.TaskPriorityChangeEvent=n)}(); | ||
!function(){var i=["user-blocking","user-visible","background"];class t{constructor(){this.channel_=new MessageChannel,this.sendPort_=this.channel_.port2,this.messages_={},this.nextMessageHandle_=1,this.channel_.port1.onmessage=i=>this.onMessageReceived_(i)}queueCallback(i){var t=this.nextMessageHandle_++;return this.messages_[t]=i,this.sendPort_.postMessage(t),t}cancelCallback(i){delete this.messages_[i]}onMessageReceived_(i){var t=i.data;if(t in this.messages_){var e=this.messages_[t];delete this.messages_[t],e()}}}function e(){return e.instance_||(e.instance_=new t),e.instance_}class r{constructor(i,t,e){void 0===e&&(e=0),this.callback_=i,this.callbackType_=null,this.handle_=null,this.canceled_=!1,this.schedule_(t,e)}isIdleCallback(){return 0===this.callbackType_}isMessageChannelCallback(){return 2===this.callbackType_}cancel(){if(!this.canceled_)switch(this.canceled_=!0,this.callbackType_){case 0:cancelIdleCallback(this.handle_);break;case 1:clearTimeout(this.handle_);break;case 2:e().cancelCallback(this.handle_);break;default:throw new TypeError("Unknown CallbackType")}}schedule_(t,r){if(r&&r>0)return this.callbackType_=1,void(this.handle_=setTimeout(()=>{this.runCallback_()},r));if(!i.includes(t))throw new TypeError("Invalid task priority : "+t);return"background"===t&&"function"==typeof requestIdleCallback?(this.callbackType_=0,void(this.handle_=requestIdleCallback(()=>{this.runCallback_()}))):"function"==typeof MessageChannel?(this.callbackType_=2,void(this.handle_=e().queueCallback(()=>{this.runCallback_()}))):(this.callbackType_=1,void(this.handle_=setTimeout(()=>{this.runCallback_()})))}runCallback_(){this.canceled_||this.callback_()}}var s=0;class n{constructor(){this.head_=null,this.tail_=null}isEmpty(){return null==this.head_}push(i){if("object"!=typeof i)throw new TypeError("Task must be an Object");i.tq_sequence_=s++,this.isEmpty()?(i.tq_prev_=null,this.head_=i):(i.tq_prev_=this.tail_,this.tail_.tq_next_=i),i.tq_next_=null,this.tail_=i}takeNextTask(){if(this.isEmpty())return null;var i=this.head_;return this.remove_(i),i}merge(i,t){if("function"!=typeof t)throw new TypeError("Must provide a selector function.");if(null==i)throw new Error("sourceQueue cannot be null");for(var e=this.head_,r=null,s=i.head_;s;){var n=s;if(s=s.tq_next_,t(n)){for(i.remove_(n);e&&e.tq_sequence_<n.tq_sequence_;)r=e,e=e.tq_next_;this.insert_(n,r),r=n}}}toArray(){for(var i=this.head_,t=[];null!==i;)t.push(i),i=i.tq_next_;return t}insert_(i,t){if(t!=this.tail_){var e=t?t.tq_next_:this.head_;i.tq_next_=e,e.tq_prev_=i,i.tq_prev_=t,null!=t?t.tq_next_=i:this.head_=i}else this.push(i)}remove_(i){if(null==i)throw new Error("Expected task to be non-null");i===this.head_&&(this.head_=i.tq_next_),i===this.tail_&&(this.tail_=this.tail_.tq_prev_),i.tq_next_&&(i.tq_next_.tq_prev_=i.tq_prev_),i.tq_prev_&&(i.tq_prev_.tq_next_=i.tq_next_)}}class a extends Event{constructor(t,e){if(!e||!i.includes(e.previousPriority))throw new TypeError("Invalid task priority: '"+e.previousPriority+"'");super(t),this.previousPriority=e.previousPriority}}class l extends AbortController{constructor(t){if(void 0===t&&(t={}),super(),null==t&&(t={}),"object"!=typeof t)throw new TypeError("'init' is not an object");var e,r,s=void 0===t.priority?"user-visible":t.priority;if(!i.includes(s))throw new TypeError("Invalid task priority: '"+s+"'");this.priority_=s,this.isPriorityChanging_=!1,r=(e=this).signal,Object.defineProperties(r,{priority:{get:function(){return e.priority_},enumerable:!0},onprioritychange:{value:null,writable:!0,enumerable:!0}}),r.addEventListener("prioritychange",i=>{r.onprioritychange&&r.onprioritychange(i)})}setPriority(t){if(!i.includes(t))throw new TypeError("Invalid task priority: "+t);if(this.isPriorityChanging_)throw new DOMException("","NotAllowedError");if(this.signal.priority!==t){this.isPriorityChanging_=!0;var e=this.priority_;this.priority_=t;var r=new a("prioritychange",{previousPriority:e});this.signal.dispatchEvent(r),this.isPriorityChanging_=!1}}}void 0===self.scheduler?(self.scheduler=new class{constructor(){this.queues_={},i.forEach(i=>{this.queues_[i]=[new n,new n]}),this.pendingHostCallback_=null,this.signals_=new WeakMap}yield(i){return(i=Object.assign({},i)).signal&&"inherit"==i.signal&&delete i.signal,i.priority&&"inherit"==i.priority&&(i.priority="user-visible"),this.postTaskOrContinuation_(()=>{},i,!0)}postTask(i,t){return this.postTaskOrContinuation_(i,t,!1)}postTaskOrContinuation_(t,e,r){if(void 0!==(e=Object.assign({},e)).signal){if(null===e.signal||!("aborted"in e.signal)||"function"!=typeof e.signal.addEventListener)return Promise.reject(new TypeError("'signal' is not a valid 'AbortSignal'"));if(e.signal&&e.signal.priority&&!i.includes(e.signal.priority))return Promise.reject(new TypeError("Invalid task priority: '"+e.signal.priority+"'"))}if(void 0!==e.priority&&(null===e.priority||!i.includes(e.priority)))return Promise.reject(new TypeError("Invalid task priority: '"+e.priority+"'"));if(void 0===e.delay&&(e.delay=0),e.delay=Number(e.delay),e.delay<0)return Promise.reject(new TypeError("'delay' must be a positive number."));var s={callback:t,options:e,resolve:null,reject:null,hostCallback:null,abortCallback:null,onTaskCompleted:function(){this.options.signal&&this.abortCallback&&(this.options.signal.removeEventListener("abort",this.abortCallback),this.abortCallback=null)},onTaskAborted:function(){this.hostCallback&&(this.hostCallback.cancel(),this.hostCallback=null),this.options.signal.removeEventListener("abort",this.abortCallback),this.abortCallback=null,this.reject(this.options.signal.reason)},isAborted:function(){return this.options.signal&&this.options.signal.aborted},isContinuation:r},n=new Promise((i,t)=>{s.resolve=i,s.reject=t});return this.schedule_(s),n}schedule_(i){var t=i.options.signal;if(t){if(t.aborted)return void i.reject(t.reason);i.abortCallback=()=>{i.onTaskAborted()},t.addEventListener("abort",i.abortCallback)}i.options.delay>0?i.hostCallback=new r(()=>{i.hostCallback=null,this.onTaskDelayExpired_(i)},null,i.options.delay):(this.pushTask_(i),this.scheduleHostCallbackIfNeeded_())}onTaskDelayExpired_(i){this.pushTask_(i),this.pendingHostCallback_&&(this.pendingHostCallback_.cancel(),this.pendingHostCallback_=null),this.schedulerEntryCallback_()}onPriorityChange_(i){var t=this.signals_.get(i);if(void 0===t)throw new Error("Attempting to change priority on an unregistered signal");if(t!==i.priority){for(var e=0;e<2;e++)this.queues_[i.priority][e].merge(this.queues_[t][e],t=>t.options.signal===i);this.signals_.set(i,i.priority)}}schedulerEntryCallback_(){this.pendingHostCallback_=null,this.runNextTask_(),this.scheduleHostCallbackIfNeeded_()}scheduleHostCallbackIfNeeded_(){var{priority:i}=this.nextTaskPriority_();null!=i&&("background"!==i&&this.pendingHostCallback_&&this.pendingHostCallback_.isIdleCallback()&&(this.pendingHostCallback_.cancel(),this.pendingHostCallback_=null),this.pendingHostCallback_||(this.pendingHostCallback_=new r(()=>{this.schedulerEntryCallback_()},i,0)))}pushTask_(t){var e;if(!i.includes(e=t.options.priority?t.options.priority:t.options.signal&&t.options.signal.priority?t.options.signal.priority:"user-visible"))throw new TypeError("Invalid task priority: "+e);if(t.options.signal&&t.options.signal.priority){var r=t.options.signal;this.signals_.has(r)||(r.addEventListener("prioritychange",()=>{this.onPriorityChange_(r)}),this.signals_.set(r,r.priority))}this.queues_[e][t.isContinuation?0:1].push(t)}runNextTask_(){var i=null;do{var{priority:t,type:e}=this.nextTaskPriority_();if(null==t)return;i=this.queues_[t][e].takeNextTask()}while(i.isAborted());try{var r=i.callback();i.resolve(r)}catch(t){i.reject(t)}finally{i.onTaskCompleted()}}nextTaskPriority_(){for(var t=0;t<i.length;t++)for(var e=i[t],r=0;r<2;r++)if(!this.queues_[e][r].isEmpty())return{priority:e,type:r};return{priority:null,type:0}}},self.TaskController=l,self.TaskPriorityChangeEvent=a):self.scheduler.yield||(self.scheduler.yield=function(i){var t=i=>i&&"user-visible"!=i?i:"user-blocking";if((i=Object.assign({},i)).signal&&"inherit"==i.signal&&delete i.signal,i.priority&&"inherit"==i.priority&&delete i.priority,i.signal&&i.signal.aborted)return Promise.reject(i.signal.reason);var e=i.priority;!e&&i.signal&&i.signal.priority&&(e=i.signal.priority),e=t(e);var r={inputSignal:i.signal,controller:new self.TaskController({priority:e}),abortCallback:null,priorityCallback:null,onTaskAborted:function(){this.controller.abort(this.inputSignal.reason),this.abortCallback=null},onPriorityChange:function(){this.controller.setPriority(t(this.inputSignal.priority))},onTaskCompleted:function(){this.abortCallback&&(this.inputSignal.removeEventListener("abort",this.abortCallback),this.abortCallback=null),this.priorityCallback&&(this.inputSignal.removeEventListener("prioritychange",this.priorityCallback),this.priorityCallback=null)}};i.signal&&(r.abortCallback=()=>{r.onTaskAborted()},i.signal.addEventListener("abort",r.abortCallback)),i.signal&&i.signal.priority&&!i.priority&&(r.priorityCallback=()=>{r.onPriorityChange()},i.signal.addEventListener("prioritychange",r.priorityCallback));var s=self.scheduler.postTask(()=>{},{signal:r.controller.signal});return s.then(()=>{r.onTaskCompleted()}).catch(i=>{throw r.onTaskCompleted(),i}),s})}(); | ||
//# sourceMappingURL=scheduler-polyfill.js.map |
{ | ||
"name": "scheduler-polyfill", | ||
"version": "1.2.1", | ||
"version": "1.3.0-beta.0", | ||
"description": "Polyfill of self.scheduler API", | ||
@@ -5,0 +5,0 @@ "main": "./dist/scheduler-polyfill.js", |
@@ -12,2 +12,4 @@ # Scheduler Polyfill | ||
## `scheduler.postTask()` | ||
The implementation uses a combination of `setTimeout`, `MessageChannel`, and | ||
@@ -17,2 +19,49 @@ `requestIdleCallback` to implement task scheduling, falling back to `setTimeout` | ||
The polyfill, like the native implementation, runs `scheduler` tasks in | ||
descending priority order (`'user-blocking'` > `'user-visible'` > | ||
`'background'`). But there are some differences in the relative order of | ||
non-`scheduler` tasks: | ||
- `"background"` tasks are scheduled using `requestIdleCallback` on browsers | ||
that support it, which provides similar scheduling as `scheduler`. For | ||
browsers that don't support it, these tasks do not have low/idle event loop | ||
priority. | ||
- `"user-blocking"` tasks have the same event loop scheduling prioritization as | ||
`"user-visible"` (similar to `setTimeout()`), meaning these tasks do not have | ||
a higher event loop priority. | ||
## `scheduler.yield()` | ||
> `scheduler.yield()` is available in version 1.3 of the polyfill, published | ||
under the `next` tag on npm. See the [Usage section](#usage) for installation | ||
instructions. | ||
The polyfill supports [`scheduler.yield()`](https://chromestatus.com/feature/6266249336586240), | ||
which is currently in [Origin Trial in | ||
Chrome](https://developer.chrome.com/origintrials/#/view_trial/836543630784069633). | ||
[Signal and priority | ||
inheritance](https://github.com/WICG/scheduling-apis/blob/main/explainers/yield-and-continuation.md#controlling-continuation-priority-and-abort-behavior) | ||
is not currently supported in the polyfill, and using the `"inherit"` option | ||
will result in the default behavior, i.e. the continuation will not be aborted | ||
and will run at default priority. | ||
The scheduling behavior of the polyfill depends on whether the browser supports | ||
`scheduler.postTask()` (i.e. older Chrome versions). If it does, then `yield()` | ||
is polyfilled with `postTask()`, with the following behavior: | ||
- `"user-visible"` continuations run as `"user-blocking"` `scheduler` tasks. | ||
This means they typically have a higher event loop priority than other tasks | ||
(consistent with `yield()`), but they can be interleaved with other | ||
`"user-blocking"` tasks. The same goes for `"user-blocking"` continuations. | ||
- `"background"` continuations are scheduled as `"background"` tasks, which | ||
means they have lowered event loop priority but don't go ahead of other | ||
`"background"` tasks, so they can be interleaved. | ||
On browsers that don't support `scheduler.postTask()`, the same event loop | ||
prioritization as the `postTask()` polyfill applies (see above), but | ||
continuations have higher priority than tasks of the same priority, e.g. | ||
`"background"` continuations run before `"background"` tasks. | ||
## Requirements | ||
@@ -24,14 +73,29 @@ | ||
**Include via unpkg:** | ||
### Include via unpkg | ||
**Use the next version of the polyfill, which includes `scheduler.yield()`:** | ||
```html | ||
<script src="https://unpkg.com/scheduler-polyfill@next"></script> | ||
``` | ||
**or use the current stable release of the polyfill:** | ||
```html | ||
<script src="https://unpkg.com/scheduler-polyfill"></script> | ||
``` | ||
**Using with npm and a bundler**: | ||
### Include via npm and a bundler | ||
```console | ||
npm install scheduler-polyfill@next | ||
``` | ||
**Or use the stable release (without `scheduler.yield()`):** | ||
```sh | ||
npm install scheduler-polyfill | ||
``` | ||
Import to populate the task scheduling global variables, if not already | ||
available in the executing browser: | ||
```js | ||
@@ -41,3 +105,3 @@ import 'scheduler-polyfill'; | ||
**Building from source:** | ||
### Building from source | ||
@@ -44,0 +108,0 @@ ```console |
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
75474
37
119
1