astoilkov / main-thread-scheduling

Fast and consistently responsive apps using a single function call
MIT License
1.29k stars 32 forks source link

Possible to manually cancel a scheduled function #4

Closed ScriptedAlchemy closed 7 months ago

ScriptedAlchemy commented 2 years ago

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;
  }
}
astoilkov commented 2 years ago

In this specific case calling requestIdleCallback() and calling yieldControl() is equivalent and there isn't a benefit in using yieldControl().

However, I see that there could be scenarios where you would want to cancel yieldControl(). This could be done with different tactics that are used when canceling promises. Here's a quick example of how this could be done using AbortSignal without new features in main-thread-scheduling:

async function requestDoWork(signal) {
  await yieldControl('background')

  if (signal.aborted) {
    return
  }

  // do work
}

const controller = new AbortController()
requestDoWork(controller.signal)

// if you want to cancel the work just call controller.abort()
astoilkov commented 2 years ago

I will be implementing this.

astoilkov commented 7 months ago

I've implemented a yieldOrContinue(strategy, signal?) property and published a new release. Any feedback is welcome.