Skip to content
This repository has been archived by the owner on Jan 15, 2021. It is now read-only.

Add timeout for fetching tokens #1644

Merged
merged 2 commits into from
Nov 30, 2020
Merged

Add timeout for fetching tokens #1644

merged 2 commits into from
Nov 30, 2020

Conversation

anxolin
Copy link
Contributor

@anxolin anxolin commented Nov 28, 2020

We've observed in two occasions now that some Metamask fails to resolve some promise for contract calls. First with the issue of SNX/sUSD proxy deprecation, yesterday with Ledger MM (not it seems it doesn't do that anymore).

When this happens, we neither get a success or an error.

Currently, if for one token, the promise is not resolved, the overall "fetch all balances" promise don't resolve either (uses Promise.all)

This PR introduces a timeout for fetching individual tokens for this extreme cases. It uses 10s, maybe it could be less. But theoretically promises should resolve one way or the other!

@ghost
Copy link

ghost commented Nov 28, 2020

Travis automatic deployment:

Copy link
Contributor

@Velenir Velenir left a comment

Choose a reason for hiding this comment

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

I understand that this was made to be a quicker-than-quick fix. I'll make a PR with improvements
#1645

Comment on lines 222 to 229
return new Promise((resolve, rejects) =>
setTimeout(() => {
if (result) {
resolve(result)
} else {
rejects(new Error(timeoutMsg))
}
}, time),
Copy link
Contributor

Choose a reason for hiding this comment

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

We already have delay() util to do that

timeoutErrorMsg?: string
}

export function timeout<T>(params: TimeoutParams<T>): Promise<T> {
Copy link
Contributor

Choose a reason for hiding this comment

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

Types are not exactly correct. You intentionally throw (return rejected Promise) for some T. Typescript can detect that if you do an overload type, for example

fetchBalancesForToken(token, userAddress, contractAddress, networkId)
.then((balance) => {
const balancePromises: Promise<TokenBalanceDetails | null>[] = tokens.map((token) => {
const timeoutPromise = timeout<TokenBalanceDetails | null>({
Copy link
Contributor

Choose a reason for hiding this comment

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

But this isn't Promise<TokenBalanceDetails | null>, this is Promise<never> as it will throw, always.

const balances = await Promise.all(balancePromises)

// TODO: Would be better to show the errored tokens in error state
return balances.filter(Boolean) as TokenBalanceDetails[]
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't have to cast if we use a type-guard predicate notEmpty, which you made precisely for these cases, didn't you?

logDebug('Using cached value for', token, userAddress, contractAddress)
return cachedValue
}
const cacheKey = constructCacheKey({ token, userAddress, contractAddress, networkId })
Copy link
Contributor

Choose a reason for hiding this comment

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

No real need to have constructCacheKey(...) in two places

* simplify type without casting

* default delay() return to void

* simplify timeout function logic

* improve timeout types

* use new timeout with automatic rejected Promise detection

* construct cacheKey once only

* throw proper Error
@Velenir Velenir merged commit 04fafbb into master Nov 30, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants