Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch from defer to PromiseWithResolvers #29078

Draft
wants to merge 2 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions src/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ Please see LICENSE files in the repository root for full details.
import React, { StrictMode } from "react";
import { createRoot, Root } from "react-dom/client";
import classNames from "classnames";
import { IDeferred, defer } from "matrix-js-sdk/src/utils";
import { TypedEventEmitter } from "matrix-js-sdk/src/matrix";
import { Glass, TooltipProvider } from "@vector-im/compound-web";

Expand Down Expand Up @@ -45,7 +44,7 @@ export interface IModal<C extends ComponentType> {
onFinished: ComponentProps<C>["onFinished"];
close(...args: Parameters<ComponentProps<C>["onFinished"]>): void;
hidden?: boolean;
deferred?: IDeferred<Parameters<ComponentProps<C>["onFinished"]>>;
deferred?: PromiseWithResolvers<Parameters<ComponentProps<C>["onFinished"]>>;
}

export interface IHandle<C extends ComponentType> {
Expand Down Expand Up @@ -214,7 +213,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
modal: IModal<C>,
props?: ComponentProps<C>,
): [IHandle<C>["close"], IHandle<C>["finished"]] {
modal.deferred = defer<Parameters<ComponentProps<C>["onFinished"]>>();
modal.deferred = Promise.withResolvers<Parameters<ComponentProps<C>["onFinished"]>>();
return [
async (...args: Parameters<ComponentProps<C>["onFinished"]>): Promise<void> => {
if (modal.beforeClosePromise) {
Expand Down
4 changes: 2 additions & 2 deletions src/SlidingSyncManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import {
SlidingSync,
} 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";

import SettingsStore from "./settings/SettingsStore";
import SlidingSyncController from "./settings/controllers/SlidingSyncController";
Expand Down Expand Up @@ -110,7 +110,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;
Expand Down
6 changes: 2 additions & 4 deletions src/WorkerManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, IDeferred } from "matrix-js-sdk/src/utils";

import { WorkerPayload } from "./workers/worker";

export class WorkerManager<Request extends {}, 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;
Expand All @@ -30,7 +28,7 @@ export class WorkerManager<Request extends {}, 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;
Expand Down
3 changes: 1 addition & 2 deletions src/audio/Playback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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]));
Expand Down
8 changes: 4 additions & 4 deletions src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
SyncStateData,
TimelineEvents,
} from "matrix-js-sdk/src/matrix";
import { defer, IDeferred, QueryDict } from "matrix-js-sdk/src/utils";
import { QueryDict } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";
import { throttle } from "lodash";
import { CryptoEvent, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
Expand Down Expand Up @@ -217,7 +217,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;
Expand Down Expand Up @@ -255,7 +255,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;
Expand Down Expand Up @@ -1503,7 +1503,7 @@ 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
this.firstSyncComplete = false;
this.firstSyncPromise = defer();
this.firstSyncPromise = Promise.withResolvers();
const cli = MatrixClientPeg.safeGet();

// Allow the JS SDK to reap timeline events. This reduces the amount of
Expand Down
3 changes: 1 addition & 2 deletions src/components/views/dialogs/MessageEditHistoryDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.

import React from "react";
import { MatrixEvent, EventType, RelationType, 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";
Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 1 addition & 2 deletions src/components/views/rooms/Autocomplete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import React, { createRef, KeyboardEvent, RefObject } from "react";
import classNames from "classnames";
import { flatMap } from "lodash";
import { Room } from "matrix-js-sdk/src/matrix";
import { defer } from "matrix-js-sdk/src/utils";

import Autocompleter, { ICompletion, ISelectionRange, IProviderCompletions } from "../../../autocomplete/Autocompleter";
import SettingsStore from "../../../settings/SettingsStore";
Expand Down Expand Up @@ -173,7 +172,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
}
}

const deferred = defer<void>();
const deferred = Promise.withResolvers<void>();
this.setState(
{
completions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 { discoverAndValidateOIDCIssuerWellKnown, 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";
Expand Down Expand Up @@ -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);
});
Expand Down
3 changes: 1 addition & 2 deletions src/settings/handlers/AccountSettingsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
*/

import { AccountDataEvents, ClientEvent, MatrixClient, MatrixEvent } from "matrix-js-sdk/src/matrix";
import { defer } from "matrix-js-sdk/src/utils";
import { isEqual } from "lodash";

import MatrixClientBackedSettingsHandler from "./MatrixClientBackedSettingsHandler";
Expand Down Expand Up @@ -159,7 +158,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;
Expand Down
3 changes: 1 addition & 2 deletions src/settings/handlers/RoomAccountSettingsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
*/

import { MatrixClient, MatrixEvent, 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";
Expand Down Expand Up @@ -96,7 +95,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;
Expand Down
3 changes: 1 addition & 2 deletions src/settings/handlers/RoomSettingsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.
*/

import { MatrixClient, MatrixEvent, RoomState, RoomStateEvent, 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";
Expand Down Expand Up @@ -92,7 +91,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);
Expand Down
5 changes: 2 additions & 3 deletions src/utils/MultiInviter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ Please see LICENSE files in the repository root for full details.

import { MatrixError, MatrixClient, EventType } from "matrix-js-sdk/src/matrix";
import { KnownMembership } from "matrix-js-sdk/src/types";
import { defer, IDeferred } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";

import { AddressType, getAddressType } from "../UserAddress";
Expand Down Expand Up @@ -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;

/**
Expand Down Expand Up @@ -93,7 +92,7 @@ export default class MultiInviter {
};
}
}
this.deferred = defer<CompletionStates>();
this.deferred = Promise.withResolvers<CompletionStates>();
this.inviteMore(0);

return this.deferred.promise;
Expand Down
6 changes: 2 additions & 4 deletions src/utils/Timer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 { IDeferred, defer } from "matrix-js-sdk/src/utils";

/**
A countdown timer, exposing a promise api.
A timer starts in a non-started state,
Expand All @@ -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();
Expand All @@ -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;
});
Expand Down
3 changes: 1 addition & 2 deletions src/utils/exportUtils/HtmlExport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions src/vector/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 2 additions & 3 deletions src/vector/platform/IPCManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, IDeferred } from "matrix-js-sdk/src/utils";
import { logger } from "matrix-js-sdk/src/logger";

import { ElectronChannel } from "../../@types/global";
Expand All @@ -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(
Expand All @@ -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 });
Expand Down
3 changes: 1 addition & 2 deletions test/unit-tests/ContentMessages-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ Please see LICENSE files in the repository root for full details.
import { mocked } from "jest-mock";
import { ISendEventResponse, MatrixClient, RelationType, UploadResponse } from "matrix-js-sdk/src/matrix";
import { ImageInfo } from "matrix-js-sdk/src/types";
import { defer } from "matrix-js-sdk/src/utils";
import encrypt, { IEncryptedFile } from "matrix-encrypt-attachment";

import ContentMessages, { UploadCanceledError, uploadFile } from "../../src/ContentMessages";
Expand Down Expand Up @@ -333,7 +332,7 @@ describe("ContentMessages", () => {

describe("cancelUpload", () => {
it("should cancel in-flight upload", async () => {
const deferred = defer<UploadResponse>();
const deferred = Promise.withResolvers<UploadResponse>();
mocked(client.uploadContent).mockReturnValue(deferred.promise);
const file1 = new File([], "file1");
const prom = contentMessages.sendContentToRoom(file1, roomId, undefined, client, undefined);
Expand Down
8 changes: 4 additions & 4 deletions test/unit-tests/components/structures/MatrixChat-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { completeAuthorizationCodeGrant } from "matrix-js-sdk/src/oidc/authorize
import { logger } from "matrix-js-sdk/src/logger";
import { OidcError } from "matrix-js-sdk/src/oidc/error";
import { BearerTokenResponse } from "matrix-js-sdk/src/oidc/validate";
import { defer, IDeferred, sleep } from "matrix-js-sdk/src/utils";
import { sleep } from "matrix-js-sdk/src/utils";
import { CryptoEvent, UserVerificationStatus } from "matrix-js-sdk/src/crypto-api";

import MatrixChat from "../../../../src/components/structures/MatrixChat";
Expand Down Expand Up @@ -79,7 +79,7 @@ describe("<MatrixChat />", () => {
const deviceId = "qwertyui";
const accessToken = "abc123";
const refreshToken = "def456";
let bootstrapDeferred: IDeferred<void>;
let bootstrapDeferred: PromiseWithResolvers<void>;
// reused in createClient mock below
const getMockClientMethods = () => ({
...mockClientMethodsUser(userId),
Expand Down Expand Up @@ -245,7 +245,7 @@ describe("<MatrixChat />", () => {
{} as ValidatedServerConfig,
);

bootstrapDeferred = defer();
bootstrapDeferred = Promise.withResolvers();

await clearAllModals();
});
Expand Down Expand Up @@ -1439,7 +1439,7 @@ describe("<MatrixChat />", () => {
jest.spyOn(MatrixJs, "createClient").mockReturnValue(client);

// intercept initCrypto and have it block until we complete the deferred
const initCryptoCompleteDefer = defer();
const initCryptoCompleteDefer = Promise.withResolvers<void>();
const initCryptoCalled = new Promise<void>((resolve) => {
client.initRustCrypto.mockImplementation(() => {
resolve();
Expand Down
Loading
Loading