Switch from defer to Promise.withResolvers (#29078)

* Switch from defer to PromiseWithResolvers

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add modernizr check

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Iterate

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2025-05-08 11:03:43 +01:00
committed by GitHub
parent 0f783ede5e
commit a3f5d207de
34 changed files with 61 additions and 89 deletions

View File

@@ -10,7 +10,6 @@ Please see LICENSE files in the repository root for full details.
import React, { StrictMode } from "react";
import { createRoot, type Root } from "react-dom/client";
import classNames from "classnames";
import { type IDeferred, defer } from "matrix-js-sdk/src/utils";
import { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
import { Glass, TooltipProvider } from "@vector-im/compound-web";
@@ -70,7 +69,7 @@ export interface IModal<C extends ComponentType> {
/** A deferred to resolve when the dialog closes, with the results as provided by
* the call to {@link close} (normally from the `onFinished` callback).
*/
deferred?: IDeferred<OnFinishedParams<C> | []>;
deferred?: PromiseWithResolvers<OnFinishedParams<C> | []>;
}
/** The result of {@link Modal.createDialog}.
@@ -254,7 +253,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
}
private getCloseFn<C extends ComponentType>(modal: IModal<C>): [IHandle<C>["close"], IHandle<C>["finished"]] {
modal.deferred = defer<OnFinishedParams<C> | []>();
modal.deferred = Promise.withResolvers<OnFinishedParams<C> | []>();
return [
async (...args: OnFinishedParams<C>): Promise<void> => {
if (modal.beforeClosePromise) {

View File

@@ -49,7 +49,7 @@ import {
SlidingSyncState,
} from "matrix-js-sdk/src/sliding-sync";
import { logger } from "matrix-js-sdk/src/logger";
import { defer, sleep } from "matrix-js-sdk/src/utils";
import { sleep } from "matrix-js-sdk/src/utils";
// how long to long poll for
const SLIDING_SYNC_TIMEOUT_MS = 20 * 1000;
@@ -184,7 +184,7 @@ export class SlidingSyncManager {
public slidingSync?: SlidingSync;
private client?: MatrixClient;
private configureDefer = defer<void>();
private configureDefer = Promise.withResolvers<void>();
public static get instance(): SlidingSyncManager {
return SlidingSyncManager.internalInstance;

View File

@@ -6,14 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { defer, type IDeferred } from "matrix-js-sdk/src/utils";
import { type WorkerPayload } from "./workers/worker";
export class WorkerManager<Request extends object, Response> {
private readonly worker: Worker;
private seq = 0;
private pendingDeferredMap = new Map<number, IDeferred<Response>>();
private pendingDeferredMap = new Map<number, PromiseWithResolvers<Response>>();
public constructor(worker: Worker) {
this.worker = worker;
@@ -30,7 +28,7 @@ export class WorkerManager<Request extends object, Response> {
public call(request: Request): Promise<Response> {
const seq = this.seq++;
const deferred = defer<Response>();
const deferred = Promise.withResolvers<Response>();
this.pendingDeferredMap.set(seq, deferred);
this.worker.postMessage({ seq, ...request });
return deferred.promise;

View File

@@ -9,7 +9,6 @@ Please see LICENSE files in the repository root for full details.
import EventEmitter from "events";
import { SimpleObservable } from "matrix-widget-api";
import { logger } from "matrix-js-sdk/src/logger";
import { defer } from "matrix-js-sdk/src/utils";
import { UPDATE_EVENT } from "../stores/AsyncStore";
import { arrayFastResample } from "../utils/arrays";
@@ -158,7 +157,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
// 5mb
logger.log("Audio file too large: processing through <audio /> element");
this.element = document.createElement("AUDIO") as HTMLAudioElement;
const deferred = defer<unknown>();
const deferred = Promise.withResolvers<unknown>();
this.element.onloadeddata = deferred.resolve;
this.element.onerror = deferred.reject;
this.element.src = URL.createObjectURL(new Blob([this.buf]));

View File

@@ -19,7 +19,7 @@ import {
type SyncStateData,
type TimelineEvents,
} from "matrix-js-sdk/src/matrix";
import { defer, type IDeferred, type QueryDict } from "matrix-js-sdk/src/utils";
import { type QueryDict } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { throttle } from "lodash";
import { CryptoEvent, type KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
@@ -215,7 +215,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
};
private firstSyncComplete = false;
private firstSyncPromise: IDeferred<void>;
private firstSyncPromise: PromiseWithResolvers<void>;
private screenAfterLogin?: IScreen;
private tokenLogin?: boolean;
@@ -254,7 +254,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// Used by _viewRoom before getting state from sync
this.firstSyncComplete = false;
this.firstSyncPromise = defer();
this.firstSyncPromise = Promise.withResolvers();
if (this.props.config.sync_timeline_limit) {
MatrixClientPeg.opts.initialSyncLimit = this.props.config.sync_timeline_limit;
@@ -1471,11 +1471,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// since we're about to start the client and therefore about to do the first sync
// We resolve the existing promise with the new one to update any existing listeners
if (!this.firstSyncComplete) {
const firstSyncPromise = defer<void>();
const firstSyncPromise = Promise.withResolvers<void>();
this.firstSyncPromise.resolve(firstSyncPromise.promise);
this.firstSyncPromise = firstSyncPromise;
} else {
this.firstSyncPromise = defer();
this.firstSyncPromise = Promise.withResolvers();
}
this.firstSyncComplete = false;
const cli = MatrixClientPeg.safeGet();

View File

@@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
import React, { type JSX } from "react";
import { type MatrixEvent, EventType, RelationType, type MatrixClient, MatrixError } from "matrix-js-sdk/src/matrix";
import { defer } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
@@ -58,7 +57,7 @@ export default class MessageEditHistoryDialog extends React.PureComponent<IProps
const eventId = this.props.mxEvent.getId()!;
const client = MatrixClientPeg.safeGet();
const { resolve, reject, promise } = defer<boolean>();
const { resolve, reject, promise } = Promise.withResolvers<boolean>();
let result: Awaited<ReturnType<MatrixClient["relations"]>>;
try {

View File

@@ -10,7 +10,6 @@ import React, { createRef, type RefObject } from "react";
import classNames from "classnames";
import { flatMap } from "lodash";
import { type Room } from "matrix-js-sdk/src/matrix";
import { defer } from "matrix-js-sdk/src/utils";
import Autocompleter, {
type ICompletion,
@@ -177,7 +176,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
}
}
const deferred = defer<void>();
const deferred = Promise.withResolvers<void>();
this.setState(
{
completions,

View File

@@ -9,7 +9,6 @@ Please see LICENSE files in the repository root for full details.
import React, { lazy, Suspense, useCallback, useContext, useEffect, useRef, useState } from "react";
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { logger } from "matrix-js-sdk/src/logger";
import { defer } from "matrix-js-sdk/src/utils";
import { _t } from "../../../../../languageHandler";
import Modal from "../../../../../Modal";
@@ -98,7 +97,7 @@ const useSignOut = (
const url = getManageDeviceUrl(delegatedAuthAccountUrl, deviceId);
window.open(url, "_blank");
} else {
const deferredSuccess = defer<boolean>();
const deferredSuccess = Promise.withResolvers<boolean>();
await deleteDevicesWithInteractiveAuth(matrixClient, deviceIds, async (success) => {
deferredSuccess.resolve(!!success);
});

View File

@@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
*/
import { type AccountDataEvents, ClientEvent, type MatrixClient, type MatrixEvent } from "matrix-js-sdk/src/matrix";
import { defer } from "matrix-js-sdk/src/utils";
import { isEqual } from "lodash";
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
@@ -162,7 +161,7 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
// Attach a deferred *before* setting the account data to ensure we catch any requests
// which race between different lines.
const deferred = defer<void>();
const deferred = Promise.withResolvers<void>();
const handler = (event: MatrixEvent): void => {
if (event.getType() !== eventType || !isEqual(event.getContent<AccountDataEvents[K]>()[field], value))
return;

View File

@@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
*/
import { type MatrixClient, type MatrixEvent, type Room, RoomEvent } from "matrix-js-sdk/src/matrix";
import { defer } from "matrix-js-sdk/src/utils";
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
import { objectClone, objectKeyChanges } from "../../utils/objects";
@@ -99,7 +98,7 @@ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettin
await this.client.setRoomAccountData(roomId, eventType, content);
const deferred = defer<void>();
const deferred = Promise.withResolvers<void>();
const handler = (event: MatrixEvent, room: Room): void => {
if (room.roomId !== roomId || event.getType() !== eventType) return;
if (field !== null && event.getContent()[field] !== value) return;

View File

@@ -14,7 +14,6 @@ import {
RoomStateEvent,
type StateEvents,
} from "matrix-js-sdk/src/matrix";
import { defer } from "matrix-js-sdk/src/utils";
import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
import { objectClone, objectKeyChanges } from "../../utils/objects";
@@ -98,7 +97,7 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl
const { event_id: eventId } = await this.client.sendStateEvent(roomId, eventType, content);
const deferred = defer<void>();
const deferred = Promise.withResolvers<void>();
const handler = (event: MatrixEvent): void => {
if (event.getId() !== eventId) return;
this.client.off(RoomStateEvent.Events, handler);

View File

@@ -21,7 +21,6 @@ import {
} from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { logger } from "matrix-js-sdk/src/logger";
import { defer } from "matrix-js-sdk/src/utils";
import { AsyncStoreWithClient } from "../AsyncStoreWithClient";
import defaultDispatcher from "../../dispatcher/dispatcher";
@@ -153,7 +152,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<EmptyObject> {
private _enabledMetaSpaces: MetaSpace[] = [];
/** Whether the feature flag is set for MSC3946 */
private _msc3946ProcessDynamicPredecessor: boolean = SettingsStore.getValue("feature_dynamic_room_predecessors");
private _storeReadyDeferred = defer();
private _storeReadyDeferred = Promise.withResolvers<void>();
public constructor() {
super(defaultDispatcher, {});

View File

@@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
import { MatrixError, type MatrixClient, EventType, type EmptyObject } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { defer, type IDeferred } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { AddressType, getAddressType } from "../UserAddress";
@@ -51,7 +50,7 @@ export default class MultiInviter {
private _fatal = false;
private completionStates: CompletionStates = {}; // State of each address (invited or error)
private errors: Record<string, IError> = {}; // { address: {errorText, errcode} }
private deferred: IDeferred<CompletionStates> | null = null;
private deferred: PromiseWithResolvers<CompletionStates> | null = null;
private reason: string | undefined;
/**
@@ -93,7 +92,7 @@ export default class MultiInviter {
};
}
}
this.deferred = defer<CompletionStates>();
this.deferred = Promise.withResolvers<CompletionStates>();
this.inviteMore(0);
return this.deferred.promise;

View File

@@ -6,8 +6,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { type IDeferred, defer } from "matrix-js-sdk/src/utils";
/**
A countdown timer, exposing a promise api.
A timer starts in a non-started state,
@@ -22,7 +20,7 @@ a new one through `clone()` or `cloneIfRun()`.
export default class Timer {
private timerHandle?: number;
private startTs?: number;
private deferred!: IDeferred<void>;
private deferred!: PromiseWithResolvers<void>;
public constructor(private timeout: number) {
this.setNotStarted();
@@ -31,7 +29,7 @@ export default class Timer {
private setNotStarted(): void {
this.timerHandle = undefined;
this.startTs = undefined;
this.deferred = defer();
this.deferred = Promise.withResolvers();
this.deferred.promise = this.deferred.promise.finally(() => {
this.timerHandle = undefined;
});

View File

@@ -13,7 +13,6 @@ import { renderToStaticMarkup } from "react-dom/server";
import { logger } from "matrix-js-sdk/src/logger";
import escapeHtml from "escape-html";
import { TooltipProvider } from "@vector-im/compound-web";
import { defer } from "matrix-js-sdk/src/utils";
import Exporter from "./Exporter";
import { mediaFromMxc } from "../../customisations/Media";
@@ -302,7 +301,7 @@ export default class HTMLExporter extends Exporter {
if (hasAvatar) await this.saveAvatarIfNeeded(mxEv);
// We have to wait for the component to be rendered before we can get the markup
// so pass a deferred as a ref to the component.
const deferred = defer<void>();
const deferred = Promise.withResolvers<void>();
const EventTile = this.getEventTile(mxEv, continuation, deferred.resolve);
let eventTileMarkup: string;

View File

@@ -49,6 +49,8 @@ function checkBrowserFeatures(): boolean {
window.Modernizr.addTest("promiseprototypefinally", () => typeof window.Promise?.prototype?.finally === "function");
// ES2020: http://262.ecma-international.org/#sec-promise.allsettled
window.Modernizr.addTest("promiseallsettled", () => typeof window.Promise?.allSettled === "function");
// ES2024: https://2ality.com/2024/05/proposal-promise-with-resolvers.html
window.Modernizr.addTest("promisewithresolvers", () => typeof window.Promise?.withResolvers === "function");
// ES2018: https://262.ecma-international.org/9.0/#sec-get-regexp.prototype.dotAll
window.Modernizr.addTest(
"regexpdotall",

View File

@@ -5,7 +5,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { defer, type IDeferred } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { type ElectronChannel } from "../../@types/global";
@@ -17,7 +16,7 @@ interface IPCPayload {
}
export class IPCManager {
private pendingIpcCalls: { [ipcCallId: number]: IDeferred<any> } = {};
private pendingIpcCalls: { [ipcCallId: number]: PromiseWithResolvers<any> } = {};
private nextIpcCallId = 0;
public constructor(
@@ -33,7 +32,7 @@ export class IPCManager {
public async call(name: string, ...args: any[]): Promise<any> {
// TODO this should be moved into the preload.js file.
const ipcCallId = ++this.nextIpcCallId;
const deferred = defer<any>();
const deferred = Promise.withResolvers<any>();
this.pendingIpcCalls[ipcCallId] = deferred;
// Maybe add a timeout to these? Probably not necessary.
window.electron!.send(this.sendChannel, { id: ipcCallId, name, args });