fromPromise sets pending value to undefined if oldPromise is also pending
I found a race in code using fromPromise. When rapidly sending new requests in quick succession, created with fromPromise, the case method will temporarily return undefined until the latest response returns. Based on the documentation, I would expect it to always return stale data and never return undefined.
Example scenario
@observable
private dataResponse = undefined;
private loadData() {
this.dataResponse = fromPromise(fetch(...), this.dataResponse);
}
getDataForReactUI() {
return this.dataResponse?.case({
fulfilled: (result) => result,
pending: (stale) => stale,
});
}
The race appears when the fetch response is slow, and the user triggers another load quickly. The sequence is:
-
loadData()called for the first time, andthis.dataResponseis initialized. - Response returns and
this.dataresolves. User sees the returned data. - User clicks a refresh button.
-
loadData()is called a second time;fromPromise()is called with the resolved promise as the second argument. - User clicks the refresh button again before the previous request returns.
-
loadData()is called a third time;fromPromise()is called with the pending promise as the second argument.
What happens:
User sees data disappear for a few seconds, before seeing the new data appear.
What I expect
User sees stale data until new data is available.
Root cause
The cause seems to be this code inside fromPromise():
https://github.com/mobxjs/mobx-utils/blob/v5.6.1/src/from-promise.ts#L185
const oldData =
oldPromise && (oldPromise as any).state === FULFILLED
? (oldPromise as any).value
: undefined
In short, if the oldPromise is still PENDING, fromPromise() will use undefined even if the oldPromise has a value for its pending state.