GithubHelp home page GithubHelp logo

astoilkov / main-thread-scheduling Goto Github PK

View Code? Open in Web Editor NEW
1.2K 8.0 30.0 377 KB

Fast and consistently responsive apps using a single function call

License: MIT License

JavaScript 1.82% TypeScript 90.17% HTML 8.01%
posttask worker background yield thread webworker defer scheduling main-thread postpone

main-thread-scheduling's Issues

Don’t crash SSR

I'm currently using a conditional to opt out on the server. But it would be less risky if on node it just performs a pass through.

I've also had some problems with jest clear timers getting stuck in a infinite loop if MTS is used on one of those unit tested components

It could be useful if node_env is set to test, do something like resolve a promise with a setTimeout. This may also help with other things like some of my tests "fail" because X function I'm spying on wasn't called fast enough.

Possible to manually cancel a scheduled function

Ive been wanting to use main thread scheduling to time aggressive updates on a function.

So in this case, if i have a update come through, then 1ms later another update is sent, id want to cancel the preexisting scheduled event and reschedule only the last valid call.

I do something like this with idleCallback but was wondering if this library could expose something similar, perhaps a label?

So if "something" already exists, and a new "something" event is sent in, remove the previous one from the queue and push this onto the end instead

class RenderWhenIdle extends React.Component {
  constructor() {
    super();
    this.updateDebounced = this.updateDebounced.bind(this);
    this.idleCallback = null;
  }

  updateDebounced() {
    this.idleCallback = requestIdleCallback(() => {
        this.forceUpdate();
    }, { timeout: 200 });
  }

  shouldComponentUpdate(nextProps) {
    if (this.idleCallback) { cancelIdleCallback(this.idleCallback); }
    this.updateDebounced();
    return false;
  }

  componentWillUnmount() {
    cancelIdleCallback(this.idleCallback);
  }

  componentDidMount() {
    this.updateDebounced();
  }

  render() {
    return this.props.children;
  }
}

callback based API

The performance overhead of the current Promise based API is not non-trivial. Any chance to get a callback API as well?

Any GC concerns when using async/await

If I use a .then() chain, I believe the browser is able to GC better compared to async await which I believe prevents automated GC

Have you come across this scenario before?

Compile errors in v6.0.0

Hi! πŸ‘‹

Firstly, thanks for your work on this project! πŸ™‚

Today I used patch-package to patch [email protected] for the project I'm working on.

Here is the diff that solved my problem:

diff --git a/node_modules/main-thread-scheduling/src/isTimeToYield.ts b/node_modules/main-thread-scheduling/src/isTimeToYield.ts
index f54e6a9..d827ae9 100644
--- a/node_modules/main-thread-scheduling/src/isTimeToYield.ts
+++ b/node_modules/main-thread-scheduling/src/isTimeToYield.ts
@@ -19,8 +19,10 @@ export default function isTimeToYield(priority: 'background' | 'user-visible'):
     }
 
     lastCallTime = now
+    // @ts-ignore: scheduling is new experimental API
+    const inputPending = navigator.scheduling?.isInputPending?.()
     lastResult =
-        now >= calculateDeadline(priority) || navigator.scheduling?.isInputPending?.() === true
+        now >= calculateDeadline(priority) || inputPending === true
 
     if (lastResult) {
         state.frameTimeElapsed = true
diff --git a/node_modules/main-thread-scheduling/src/tracking.ts b/node_modules/main-thread-scheduling/src/tracking.ts
index 033c536..14cf024 100644
--- a/node_modules/main-thread-scheduling/src/tracking.ts
+++ b/node_modules/main-thread-scheduling/src/tracking.ts
@@ -41,6 +41,7 @@ export function startTracking(): void {
                 isTracking = false
 
                 if (typeof cancelIdleCallback !== 'undefined') {
+                    // @ts-ignore: will be defined
                     cancelIdleCallback(idleCallbackId)
                 }
             } else {
diff --git a/node_modules/main-thread-scheduling/src/yieldControl.ts b/node_modules/main-thread-scheduling/src/yieldControl.ts
index 7236816..74a98b2 100644
--- a/node_modules/main-thread-scheduling/src/yieldControl.ts
+++ b/node_modules/main-thread-scheduling/src/yieldControl.ts
@@ -49,6 +49,7 @@ async function schedule(priority: 'user-visible' | 'background'): Promise<void>
         await new Promise<void>((resolve) => requestNextTask(resolve))
 
         // istanbul ignore if
+        // @ts-ignore: scheduling is new experimental API
         if (navigator.scheduling?.isInputPending?.() === true) {
             await schedule(priority)
         } else if (state.frameWorkStartTime === undefined) {

This issue body was partially generated by patch-package.

Stand-alone JS file?

Since this is a client side library, is it possible to publish a single .js file which exposes this functionality and allows for general purpose importing, without having to build a Node.js project for it?

Easier web workers

This is a cool idea for the future of web-apps, and it's nice to have a userland implementation before it's added to the standard! Since you said in the README to make an issue if there's an alternate solution to UI freezing, I'm making this issue to mention my package isoworker, which does some magic to make web workers easier to use. Most worker wrappers that allow you to avoid creating workers by hand have one key flaw: you can't easily copy anything from the main thread into the worker and must send over only a function. My package mitigates pretty much all the complexity of web workers: you can make long-running closures on a separate thread with virtually no restrictions, it almost works like calling the closure asynchronously on the main thread.

This package still seems better for cases where there is a lot of data being processed or constant communication is necessary, but isoworker can yield zero UI freezing by running on a separate thread.

Add more priorities

Currently, I have added only two priorities to the library β€” background and user-visible. I feel this won't be enough. However, I can't imagine all the use cases the library will be used in. This is why I am starting a discussion here, so together, we can find the right priorities.

You can read more about priorities here and here.

interrupt the rendering of react

This project is great, I would like to ask whether it can interrupt the rendering of react. For example, if there is a list of 10,000 length arrays and react updates this array when performing state updates, can it interrupt the rendering

Error when building a project with Vite

Hi and thanks for your work on this library !

Since version 11.0.0 (i.e. with versions 11.0.0 and 12.0.0) I have encountered problems when I compile a project with Vite (5.0.x) that uses "main-thread-scheduling".

The compilation fails with the following message and Vite stops :

✘ [ERROR] Could not resolve "./src/utils/queueTask"

    node_modules/main-thread-scheduling/index.js:7:37:
      7 β”‚ export { default as queueTask } from './src/utils/queueTask';
        β•΅                                      ~~~~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not resolve "./src/utils/withResolvers"

    node_modules/main-thread-scheduling/index.js:8:41:
      8 β”‚ export { default as withResolvers } from './src/utils/withResolvers';
        β•΅                                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not resolve "./src/utils/requestAfterFrame"

    node_modules/main-thread-scheduling/index.js:9:38:
      9 β”‚ export { default as afterFrame } from './src/utils/requestAfterFrame';
        β•΅                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not resolve "./utils/hasValidContext"

    node_modules/main-thread-scheduling/src/isTimeToYield.js:2:28:
      2 β”‚ import hasValidContext from './utils/hasValidContext';
        β•΅                             ~~~~~~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not resolve "./utils/queueTask"

    node_modules/main-thread-scheduling/src/yieldControl.js:2:22:
      2 β”‚ import queueTask from './utils/queueTask';
        β•΅                       ~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not resolve "./utils/hasValidContext"

    node_modules/main-thread-scheduling/src/yieldControl.js:4:28:
      4 β”‚ import hasValidContext from './utils/hasValidContext';
        β•΅                             ~~~~~~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not resolve "./utils/promiseEscape"

    node_modules/main-thread-scheduling/src/yieldControl.js:5:58:
      5 β”‚ ...PromiseEscape, requestPromiseEscape } from './utils/promiseEscape';
        β•΅                                               ~~~~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not resolve "./tasks/createTask"

    node_modules/main-thread-scheduling/src/yieldControl.js:6:23:
      6 β”‚ import createTask from './tasks/createTask';
        β•΅                        ~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not resolve "./tasks/removeTask"

    node_modules/main-thread-scheduling/src/yieldControl.js:7:23:
      7 β”‚ import removeTask from './tasks/removeTask';
        β•΅                        ~~~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not resolve "./tasks/nextTask"

    node_modules/main-thread-scheduling/src/yieldControl.js:8:25:
      8 β”‚ import { nextTask } from './tasks/nextTask';
        β•΅                          ~~~~~~~~~~~~~~~~~~

✘ [ERROR] Could not resolve "./utils/withResolvers"

    node_modules/main-thread-scheduling/src/schedulingState.js:1:26:
      1 β”‚ import withResolvers from './utils/withResolvers';
        β•΅                           ~~~~~~~~~~~~~~~~~~~~~~~

Error: Build failed with 11 errors:
node_modules/main-thread-scheduling/index.js:7:37: ERROR: Could not resolve "./src/utils/queueTask"
node_modules/main-thread-scheduling/index.js:8:41: ERROR: Could not resolve "./src/utils/withResolvers"
node_modules/main-thread-scheduling/index.js:9:38: ERROR: Could not resolve "./src/utils/requestAfterFrame"
node_modules/main-thread-scheduling/src/isTimeToYield.js:2:28: ERROR: Could not resolve "./utils/hasValidContext"
node_modules/main-thread-scheduling/src/schedulingState.js:1:26: ERROR: Could not resolve "./utils/withResolvers"
...
    at failureErrorWithLog (/home/projects/vitejs-vite-adh7q9/node_modules/esbuild/lib/main.js:1641:15)
    at eval (/home/projects/vitejs-vite-adh7q9/node_modules/esbuild/lib/main.js:1049:25)
    at eval (/home/projects/vitejs-vite-adh7q9/node_modules/esbuild/lib/main.js:1517:9) {
  errors: [Getter/Setter],
  warnings: [Getter/Setter]
}

It happens in a large project I'm working on but you can see a minimal repro here in a projet with only Vite, Typescript and main-thread-scheduling (this is a stackblitz project, hope this is fine to demonstrate the issue - try to change the main-thread-scheduling version to 10.0.0 in package.json to see it was working fine with older versions).

Am I doing something wrong or is there a workaround ?
Thanks !

Comparisson with other frameworks

I'll like to see a comparison between this library and other approaches followed by some frameworks, like react or solid.js (the latter being a simplified version of the former).

I know that solid for example doesn't have different priorities for its scheduler, I'll like to know how much it benefits to do the distinction between 'user-visible' and 'background' in your approach.

Can't find the effect.

I have entered the link, this CodeSandbox, can't find any difference between un-commented and commented code..Maybe my PC is powerful enought to handle the heavy task...Do u want to use some tools to test your improvements, such as tinybench?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.