Skip to content

Commit

Permalink
fix(runtime): issue with update-component and patched Promise (#4460)
Browse files Browse the repository at this point in the history
This fixes an issue with a check used in `updateComponent` to enqueue
asynchronous updates (implemented with async functions) to components.
In particular, if the global `Promise` object is patched such that it is
not equal to the native `Promise` object returned by, for instance, an
`async` function, which will return the native, unpatched object even if
`window.Promise` has been overwritten.

A previous change in #4250 made a change such that we did a check like
this:

```ts
maybePromise instanceof Promise
```

The problem is that if `maybePromise` is the return value of an async
function, like

```ts
const foo = await something();
```

and `window.Promise` has been overwritten then this check will
unfortunately fail unexpectedly.

The fix is just to first check `instanceof` and then add a duck-type-ey
fallback as well.
  • Loading branch information
alicewriteswrongs committed Jun 13, 2023
1 parent 9b6c7ea commit 1187694
Showing 1 changed file with 15 additions and 1 deletion.
16 changes: 15 additions & 1 deletion src/runtime/update-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,21 @@ const dispatchHooks = (hostRef: d.HostRef, isInitialLoad: boolean): Promise<void
* @returns either a `Promise` or the return value of the provided function
*/
const enqueue = (maybePromise: Promise<void> | undefined, fn: () => Promise<void>): Promise<void> | undefined =>
maybePromise instanceof Promise ? maybePromise.then(fn) : fn();
isPromisey(maybePromise) ? maybePromise.then(fn) : fn();

/**
* Check that a value is a `Promise`. To check, we first see if the value is an
* instance of the `Promise` global. In a few circumstances, in particular if
* the global has been overwritten, this is could be misleading, so we also do
* a little 'duck typing' check to see if the `.then` property of the value is
* defined and a function.
*
* @param maybePromise it might be a promise!
* @returns whether it is or not
*/
const isPromisey = (maybePromise: Promise<void> | unknown): maybePromise is Promise<void> =>
maybePromise instanceof Promise ||
(maybePromise && (maybePromise as any).then && typeof (maybePromise as Promise<void>).then === 'function');

const updateComponent = async (hostRef: d.HostRef, instance: any, isInitialLoad: boolean) => {
const elm = hostRef.$hostElement$ as d.RenderNode;
Expand Down

0 comments on commit 1187694

Please sign in to comment.