From 8982c7bb7647420cc71fc0082d9ff4a094e51c9c Mon Sep 17 00:00:00 2001 From: Paul Berberian Date: Wed, 8 Nov 2023 11:40:51 +0100 Subject: [PATCH] v4: Remove next-tick dependency For years, we had a unique external dependency (the only non-dev dependency) called "next-tick" allowing us to schedule callbacks in micro-tasks, either to provide asynchronous browser API shims or for some specific logic where we would prefer to bring some asynchronicity in without the need of bringing the event loop into this. We were not too happy having this dependency because the name "nextTick" didn't ring a bell to most RxPlayer developers who never really worked with node.JS before, whereas the "microtask" concept is generally more known and understood in JavaScript environments, or at least is easier to look for on the internet. Moreover, now that we consider that a Promise object has to be accessible globally in the current environment as a precondition to run the RxPlayer (so IE11 is now only supported as long as `Promise` is polyfilled by the application, which almost everyone still supporting that target in a complex media application does anyway) and that specification-following Promise implementations include a mean to schedule microtasks, we have a very easy way to provide a shim in cases where the `queueMicroTask` global function is not already supported. So this commit removes the next-tick dependency and replace it by either the natively-provided `queueMicroTask` function or a Promise-based ponyfill if not, naming the result `queueMicroTask`, which should be much more familiar to JS dev and is a simpler implementation than what `next-tick` provided, we moreover now control. Its removal also means that now the RxPlayer has no dependency from an application perspective, which may eliminates some groups of issues applications sometimes have when doing weird things with their webpack/vite configs :p We may still have an issue for Promise implementations which do not rely on microtasks (or even event loop events, we shouldn't have an issue with those), but I would expect that the very large majority of Promise polyfills in use today respect that key part of the spec. --- demo/full/scripts/globals.d.ts | 1 - package-lock.json | 13 ----------- package.json | 3 --- src/compat/patch_webkit_source_buffer.ts | 4 ++-- .../stream/adaptation/adaptation_stream.ts | 4 ++-- .../orchestrator/stream_orchestrator.ts | 6 ++--- src/core/stream/period/period_stream.ts | 4 ++-- src/typings/next-tick.d.ts | 23 ------------------- src/utils/queue_microtask.ts | 7 ++++++ 9 files changed, 16 insertions(+), 49 deletions(-) delete mode 100644 src/typings/next-tick.d.ts create mode 100644 src/utils/queue_microtask.ts diff --git a/demo/full/scripts/globals.d.ts b/demo/full/scripts/globals.d.ts index c56c2a32bd..953c8d724e 100644 --- a/demo/full/scripts/globals.d.ts +++ b/demo/full/scripts/globals.d.ts @@ -1,3 +1,2 @@ import "../../../src/typings/globals.d"; -import "../../../src/typings/next-tick"; import "../../../src/typings/object-assign"; diff --git a/package-lock.json b/package-lock.json index efcc528395..f30a485601 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,6 @@ "name": "rx-player", "version": "4.0.0-beta.3", "license": "Apache-2.0", - "dependencies": { - "next-tick": "1.1.0" - }, "devDependencies": { "@babel/core": "7.23.0", "@babel/plugin-transform-runtime": "7.22.15", @@ -11470,11 +11467,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "node_modules/next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, "node_modules/nise": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", @@ -23033,11 +23025,6 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, - "next-tick": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", - "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" - }, "nise": { "version": "5.1.4", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.4.tgz", diff --git a/package.json b/package.json index 0a174e587e..c3b5703762 100644 --- a/package.json +++ b/package.json @@ -168,9 +168,6 @@ "type": "git", "url": "git://github.com/canalplus/rx-player.git" }, - "dependencies": { - "next-tick": "1.1.0" - }, "devDependencies": { "@babel/core": "7.23.0", "@babel/plugin-transform-runtime": "7.22.15", diff --git a/src/compat/patch_webkit_source_buffer.ts b/src/compat/patch_webkit_source_buffer.ts index 32f35569d0..67d6afa3c7 100644 --- a/src/compat/patch_webkit_source_buffer.ts +++ b/src/compat/patch_webkit_source_buffer.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import nextTick from "next-tick"; import EventEmitter from "../utils/event_emitter"; +import queueMicrotask from "../utils/queue_microtask"; import globalScope from "./global_scope"; import isNode from "./is_node"; @@ -57,7 +57,7 @@ export default function patchWebkitSourceBuffer() : void { sourceBufferWebkitProto._emitUpdate = function(eventName : string, val : unknown) { - nextTick(() => { + queueMicrotask(() => { /* eslint-disable no-invalid-this */ this.trigger(eventName, val); this.updating = false; diff --git a/src/core/stream/adaptation/adaptation_stream.ts b/src/core/stream/adaptation/adaptation_stream.ts index 8414e55ff3..371e8c09aa 100644 --- a/src/core/stream/adaptation/adaptation_stream.ts +++ b/src/core/stream/adaptation/adaptation_stream.ts @@ -1,4 +1,3 @@ -import nextTick from "next-tick"; import config from "../../../config"; import { formatError } from "../../../errors"; import log from "../../../log"; @@ -7,6 +6,7 @@ import assertUnreachable from "../../../utils/assert_unreachable"; import cancellableSleep from "../../../utils/cancellable_sleep"; import noop from "../../../utils/noop"; import objectAssign from "../../../utils/object_assign"; +import queueMicrotask from "../../../utils/queue_microtask"; import SharedReference, { createMappedReference, IReadOnlySharedReference, @@ -192,7 +192,7 @@ export default function AdaptationStream( // conditions where the inner logic would be called synchronously before // the next observation (which may reflect very different playback conditions) // is actually received. - return nextTick(() => { + return queueMicrotask(() => { playbackObserver.listen(() => { if (fnCancelSignal.isCancelled()) { return; diff --git a/src/core/stream/orchestrator/stream_orchestrator.ts b/src/core/stream/orchestrator/stream_orchestrator.ts index 95a8b42586..3fecf5d682 100644 --- a/src/core/stream/orchestrator/stream_orchestrator.ts +++ b/src/core/stream/orchestrator/stream_orchestrator.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import nextTick from "next-tick"; import config from "../../../config"; import { MediaError } from "../../../errors"; import log from "../../../log"; @@ -22,6 +21,7 @@ import Manifest, { IDecipherabilityUpdateElement, Period, } from "../../../manifest"; +import queueMicrotask from "../../../utils/queue_microtask"; import { createMappedReference, IReadOnlySharedReference, @@ -351,7 +351,7 @@ export default function StreamOrchestrator( // Schedule micro task before checking the last playback observation // to reduce the risk of race conditions where the next observation // was going to be emitted synchronously. - nextTick(() => { + queueMicrotask(() => { if (orchestratorCancelSignal.isCancelled()) { return ; } @@ -554,7 +554,7 @@ export default function StreamOrchestrator( // conditions where the inner logic would be called synchronously before // the next observation (which may reflect very different playback // conditions) is actually received. - return nextTick(() => { + return queueMicrotask(() => { if (innerCancelSignal.isCancelled()) { return; } diff --git a/src/core/stream/period/period_stream.ts b/src/core/stream/period/period_stream.ts index fb055e4528..ee8fc37fb9 100644 --- a/src/core/stream/period/period_stream.ts +++ b/src/core/stream/period/period_stream.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import nextTick from "next-tick"; import config from "../../../config"; import { formatError, @@ -26,6 +25,7 @@ import { Period, } from "../../../manifest"; import objectAssign from "../../../utils/object_assign"; +import queueMicrotask from "../../../utils/queue_microtask"; import { getLeftSizeOfBufferedTimeRange } from "../../../utils/ranges"; import SharedReference, { IReadOnlySharedReference, @@ -362,7 +362,7 @@ export default function PeriodStream( // is actually received. // It can happen when `askForMediaSourceReload` is called as a side-effect of // the same event that triggers the playback observation to be emitted. - nextTick(() => { + queueMicrotask(() => { playbackObserver.listen(() => { if (cancelSignal.isCancelled()) { return; diff --git a/src/typings/next-tick.d.ts b/src/typings/next-tick.d.ts deleted file mode 100644 index e8288a9f87..0000000000 --- a/src/typings/next-tick.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright 2015 CANAL+ Group - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -declare module "next-tick" { - function nextTick( - fn : () => void - ) : void; - - export default nextTick; -} diff --git a/src/utils/queue_microtask.ts b/src/utils/queue_microtask.ts new file mode 100644 index 0000000000..97e6d1a4e0 --- /dev/null +++ b/src/utils/queue_microtask.ts @@ -0,0 +1,7 @@ +export default typeof queueMicrotask === "function" ? + queueMicrotask : + function queueMicrotaskPonyfill( + cb: () => void + ): void { + Promise.resolve().then(cb, () => cb()); + };