Skip to content

Commit

Permalink
Add status attribute to deferred
Browse files Browse the repository at this point in the history
  • Loading branch information
bvaughn committed Nov 27, 2023
1 parent dbb61bc commit 419b86c
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 5 deletions.
1 change: 1 addition & 0 deletions packages/suspense/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export interface Deferred<Type> {
promise: Promise<Type>;
reject(error: any): void;
resolve(value?: Type): void;
status: StatusPending | StatusRejected | StatusResolved;
}

// Cache types
Expand Down
5 changes: 5 additions & 0 deletions packages/suspense/src/utils/createDeferred.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
import { STATUS_PENDING, STATUS_REJECTED, STATUS_RESOLVED } from "..";
import { createDeferred } from "./createDeferred";

describe("createDeferred", () => {
it("should resolve with a value", async () => {
const deferred = createDeferred<string>();
expect(deferred.status).toBe(STATUS_PENDING);

setTimeout(() => {
deferred.resolve("Resolved value");
expect(deferred.status).toBe(STATUS_RESOLVED);
}, 0);

await expect(await deferred.promise).toBe("Resolved value");
});

it("should reject with a value", async () => {
const deferred = createDeferred<string>();
expect(deferred.status).toBe(STATUS_PENDING);

deferred.reject("Expected error");
expect(deferred.status).toBe(STATUS_REJECTED);

let caught = null;

Expand Down
20 changes: 15 additions & 5 deletions packages/suspense/src/utils/createDeferred.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { Deferred, Status } from "../types";
import { STATUS_PENDING, STATUS_REJECTED, STATUS_RESOLVED } from "..";
import {
Deferred,
StatusPending,
StatusRejected,
StatusResolved,
} from "../types";

// A "thenable" is a subset of the Promise API.
// We could use a Promise as thenable, but Promises have a downside: they use the microtask queue.
// An advantage to creating a custom thenable is synchronous resolution (or rejection).
//
// A "deferred" is a "thenable" that has convenience resolve/reject methods.
export function createDeferred<Type>(debugLabel?: string): Deferred<Type> {
let status: Status = "pending";
let status: StatusPending | StatusRejected | StatusResolved = STATUS_PENDING;

let rejectPromise: (error: Error) => void;
let resolvePromise: (value: Type | PromiseLike<Type>) => void;
Expand All @@ -20,7 +26,7 @@ export function createDeferred<Type>(debugLabel?: string): Deferred<Type> {
});

function assertPending() {
if (status !== "pending") {
if (status !== STATUS_PENDING) {
throw Error(`Deferred has already been ${status}`);
}
}
Expand All @@ -34,18 +40,22 @@ export function createDeferred<Type>(debugLabel?: string): Deferred<Type> {
reject(error: Error) {
assertPending();

status = "rejected";
status = STATUS_REJECTED;

rejectPromise(error);
},

resolve(value: Type) {
assertPending();

status = "resolved";
status = STATUS_RESOLVED;

resolvePromise(value);
},

get status() {
return status;
},
};

return deferred;
Expand Down

1 comment on commit 419b86c

@vercel
Copy link

@vercel vercel bot commented on 419b86c Nov 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.