Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

main-thread-scheduling

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

main-thread-scheduling - npm Package Compare versions

Comparing version 6.0.0-0 to 6.0.0

src/requestNextTask.d.ts

11

package.json
{
"name": "main-thread-scheduling",
"version": "6.0.0-0",
"description": "Consistently responsive apps while staying on the main thread",
"version": "6.0.0",
"description": "Faster apps using a simple API instead of Web Workers",
"license": "MIT",

@@ -17,2 +17,4 @@ "repository": "astoilkov/main-thread-scheduling",

"background",
"user-visible",
"task",
"idle",

@@ -26,2 +28,5 @@ "yield",

"scheduling",
"scheduler",
"isInputPending",
"MessageChannel",
"main-thread",

@@ -38,3 +43,3 @@ "event loop",

"lint": "eslint --cache --format=pretty --ext=.ts ./",
"test": "yarn run build && yarn run lint && [[ -z $CI ]] && jest --coverage --coverageReporters=text || jest --coverage",
"test": "yarn run build && yarn run lint && if [[ -z $CI ]]; then jest --coverage --coverageReporters=text; else jest --coverage; fi",
"release": "yarn run build && np",

@@ -41,0 +46,0 @@ "prettier": "prettier --write --config .prettierrc.yaml {*.ts,**/*.ts,*.json,**.json}"

@@ -11,3 +11,3 @@ <br>

<p align="center">
Consistently responsive apps while staying on the main thread
Faster apps using a simple API instead of Web Workers
</p>

@@ -17,3 +17,3 @@

<a href="https://bundlephobia.com/result?p=main-thread-scheduling">
<img src="https://badgen.net/bundlephobia/minzip/main-thread-scheduling" alt="Gzipped Size" />
<img src="https://img.shields.io/bundlephobia/minzip/main-thread-scheduling" alt="Gzipped Size" />
</a>

@@ -62,4 +62,4 @@ <a href="https://codeclimate.com/github/astoilkov/main-thread-scheduling/test_coverage">

- **Not a weekend project.** I've already been using it for over a year in the core of two of my products — [Nota](https://nota.md) and [iBar](https://ibar.app).
- **This is the future.** Browsers are probably going to support scheduling tasks on the main thread in the future. Here is the [spec](https://github.com/WICG/scheduling-apis). This library will still be relevant in the future because it provides an easier API.
- **Simple.** 90% of the time you only need `yieldOrContinue(priority)` function. The API has two more functions for more advanced cases.
- **This is the future.** Browsers are probably going to support scheduling tasks on the main thread in the future. Here is the [spec](https://github.com/WICG/scheduling-apis). This library will still be relevant in the future — [explanation](#scheduler-yield-alternative).
- **Simple.** 90% of the time you only need the `yieldOrContinue(priority)` function. The API has two more functions for more advanced cases.
- **High quality.** Aiming for high-quality with [my open-source principles](https://astoilkov.com/my-open-source-principles).

@@ -121,6 +121,8 @@

**Web Workers** are a great alternative if you have: 1) heavy code (e.g. image processing), 2) something that isn't a task but a process (runs through a big time of the app lifecycle). However, in reality, it's rare to see people using them. That's because they require significant investment of time due to the complexity that can't be avoided when working with CPU threads regardless of the programming language. This library can be used as a gateway before transitioning to Web Workers. In reality, a lot of the times, you would discover the doing it on the main thread is good enough.
**Web Workers** are a great fit if you have: 1) heavy algorithm (e.g. image processing), 2) heavy process (runs for a long time, big part of the app lifecycle). However, in reality, it's rare to see people using them. That's because they require significant investment of time due to the complexity that can't be avoided when working with CPU threads regardless of the programming language. This library can be used as a gateway before transitioning to Web Workers. In reality, a lot of the times, you would discover the doing it on the main thread is good enough.
<div id="scheduler-yield-alternative"></div>
**[`scheduler.yield()`](https://github.com/WICG/scheduling-apis/blob/main/explainers/yield-and-continuation.md)** will probably land in browsers at some point. However, is `scheduler.yield()` enough? The spec isn't very clear on how it will work exactly so I'm not sure. My guess is that it would be possible go without this library but you will need extra code to do so. That's because you will need to reimplement the `isTimeToYield()` method for which I don't see an alternative in the [spec](https://github.com/WICG/scheduling-apis).
**[React scheduler](https://github.com/facebook/react/blob/main/packages/scheduler/README.md)** is a similar implementation. They plan to make it more generic (for use outside of React) but there doesn't seem to be a public roadmap for that.

@@ -1,3 +0,2 @@

import { getLastIdleDeadline } from './idleFrameTracking';
import { getPerFrameScheduleStartTime } from './animationFrameTracking';
import state from './state';
// #performance

@@ -16,3 +15,3 @@ // calling `isTimeToYield()` thousand of times is slow. `lastCall` helps to run logic inside of

const now = Date.now();
if (now - lastCallTime === 0) {
if (!lastResult && now - lastCallTime === 0) {
return lastResult;

@@ -23,7 +22,9 @@ }

now >= calculateDeadline(priority) || ((_b = (_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending) === null || _b === void 0 ? void 0 : _b.call(_a)) === true;
if (lastResult) {
state.frameTimeElapsed = true;
}
return lastResult;
}
function calculateDeadline(priority) {
const perFrameScheduleStartTime = getPerFrameScheduleStartTime();
if (perFrameScheduleStartTime === undefined) {
if (state.frameWorkStartTime === undefined) {
// silentError()

@@ -35,10 +36,9 @@ return -1;

// Math.round(100 - (1000/60)) = Math.round(83,333) = 83
return perFrameScheduleStartTime + 83;
return state.frameWorkStartTime + 83;
}
case 'background': {
const lastIdleDeadline = getLastIdleDeadline();
const idleDeadline = lastIdleDeadline === undefined
const idleDeadline = state.idleDeadline === undefined
? Number.MAX_SAFE_INTEGER
: Date.now() + lastIdleDeadline.timeRemaining();
return Math.min(perFrameScheduleStartTime + 5, idleDeadline);
: Date.now() + state.idleDeadline.timeRemaining();
return Math.min(state.frameWorkStartTime + 5, idleDeadline);
}

@@ -45,0 +45,0 @@ // istanbul ignore next

@@ -1,2 +0,1 @@

import requestLaterMicrotask from './requestLaterMicrotask';
let globalId = 0;

@@ -12,7 +11,9 @@ const running = new Set();

running.add(id);
requestLaterMicrotask(() => {
if (running.has(id)) {
callback();
running.delete(id);
}
queueMicrotask(() => {
queueMicrotask(() => {
if (running.has(id)) {
callback();
running.delete(id);
}
});
});

@@ -19,0 +20,0 @@ globalId += 1;

@@ -1,7 +0,6 @@

import nextTask from './nextTask';
import state from './state';
import isTimeToYield from './isTimeToYield';
import requestLaterMicrotask from './requestLaterMicrotask';
import { notifyScheduleComplete } from './animationFrameTracking';
import requestNextTask from './requestNextTask';
import { createTask, nextTask, removeTask } from './tasks';
import { cancelPromiseEscape, requestPromiseEscape } from './promiseEscape';
import { createDeferred, isDeferredLast, nextDeferred, removeDeferred } from './deferred';
let promiseEscapeId;

@@ -20,6 +19,6 @@ /**

cancelPromiseEscape(promiseEscapeId);
const deferred = createDeferred(priority);
const task = createTask(priority);
await schedule(priority);
if (!isDeferredLast(deferred)) {
await deferred.ready;
if (state.tasks[0] !== task) {
await task.ready;
if (isTimeToYield(priority)) {

@@ -29,31 +28,31 @@ await schedule(priority);

}
removeDeferred(deferred);
removeTask(task);
cancelPromiseEscape(promiseEscapeId);
promiseEscapeId = requestPromiseEscape(() => {
nextDeferred();
nextTask();
});
}
async function schedule(priority) {
var _a, _b;
if (state.frameTimeElapsed) {
await state.onAnimationFrame.promise;
}
if (priority === 'user-visible' || typeof requestIdleCallback === 'undefined') {
await waitCallback(requestLaterMicrotask);
while (true) {
await waitCallback(nextTask);
notifyScheduleComplete();
if (!isTimeToYield(priority)) {
break;
}
await new Promise((resolve) => requestNextTask(resolve));
// istanbul ignore if
if (((_b = (_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending) === null || _b === void 0 ? void 0 : _b.call(_a)) === true) {
await schedule(priority);
}
else if (state.frameWorkStartTime === undefined) {
state.frameWorkStartTime = Date.now();
}
}
else {
await waitCallback(requestLaterMicrotask);
await waitCallback(requestIdleCallback);
notifyScheduleComplete();
await state.onIdleCallback.promise;
// not checking for `navigator.scheduling?.isInputPending?.()` here because idle callbacks
// ensure no input is pending
if (state.frameWorkStartTime === undefined) {
state.frameWorkStartTime = Date.now();
}
}
}
async function waitCallback(callback) {
return await new Promise((resolve) => {
callback(() => {
resolve();
});
});
}

@@ -0,3 +1,3 @@

import yieldControl from './yieldControl';
import isTimeToYield from './isTimeToYield';
import yieldControl from './yieldControl';
// disabling ESLint otherwise `requestPromiseEscape()` in `yieldControl()` won't work

@@ -4,0 +4,0 @@ // eslint-disable-next-line @typescript-eslint/promise-function-async

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc