( thenableState: ThenableState, thenable: Thenable<T>, index: number, )
| 43 | } |
| 44 | |
| 45 | export function trackUsedThenable<T>( |
| 46 | thenableState: ThenableState, |
| 47 | thenable: Thenable<T>, |
| 48 | index: number, |
| 49 | ): T { |
| 50 | const previous = thenableState[index]; |
| 51 | if (previous === undefined) { |
| 52 | thenableState.push(thenable); |
| 53 | } else { |
| 54 | if (previous !== thenable) { |
| 55 | // Reuse the previous thenable, and drop the new one. We can assume |
| 56 | // they represent the same value, because components are idempotent. |
| 57 | |
| 58 | // Avoid an unhandled rejection errors for the Promises that we'll |
| 59 | // intentionally ignore. |
| 60 | thenable.then(noop, noop); |
| 61 | thenable = previous; |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | // We use an expando to track the status and result of a thenable so that we |
| 66 | // can synchronously unwrap the value. Think of this as an extension of the |
| 67 | // Promise API, or a custom interface that is a superset of Thenable. |
| 68 | // |
| 69 | // If the thenable doesn't have a status, set it to "pending" and attach |
| 70 | // a listener that will update its status and result when it resolves. |
| 71 | switch (thenable.status) { |
| 72 | case 'fulfilled': { |
| 73 | const fulfilledValue: T = thenable.value; |
| 74 | return fulfilledValue; |
| 75 | } |
| 76 | case 'rejected': { |
| 77 | const rejectedError = thenable.reason; |
| 78 | throw rejectedError; |
| 79 | } |
| 80 | default: { |
| 81 | if (typeof thenable.status === 'string') { |
| 82 | // Only instrument the thenable if the status if not defined. If |
| 83 | // it's defined, but an unknown value, assume it's been instrumented by |
| 84 | // some custom userspace implementation. We treat it as "pending". |
| 85 | // Attach a dummy listener, to ensure that any lazy initialization can |
| 86 | // happen. Flight lazily parses JSON when the value is actually awaited. |
| 87 | thenable.then(noop, noop); |
| 88 | } else { |
| 89 | const pendingThenable: PendingThenable<T> = (thenable: any); |
| 90 | pendingThenable.status = 'pending'; |
| 91 | pendingThenable.then( |
| 92 | fulfilledValue => { |
| 93 | if (thenable.status === 'pending') { |
| 94 | const fulfilledThenable: FulfilledThenable<T> = (thenable: any); |
| 95 | fulfilledThenable.status = 'fulfilled'; |
| 96 | fulfilledThenable.value = fulfilledValue; |
| 97 | } |
| 98 | }, |
| 99 | (error: mixed) => { |
| 100 | if (thenable.status === 'pending') { |
| 101 | const rejectedThenable: RejectedThenable<T> = (thenable: any); |
| 102 | rejectedThenable.status = 'rejected'; |
no test coverage detected