fetchOptions and AbortController #1590
-
Hello We are using urql to create a hybrid app where we use the offlineExchange for some optimistic submissions, but require the user online for important submissions. Because we use the offlineExchange network errors do not bubble up, and fetch requests never timeout if the device is offline. To get around this I am trying to use the fetchOptions to setup an AbortController to timeout requests after X seconds (for testing its set to 1 second) The code looks like this
The problem is that after 1 second the request does not time out as expected. Has anyone had any success in timing out a fetch request when using the offlineExchange? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
There's a couple of things to unpack here and the
This thread had a couple of explanations on the specific error behaviour: #1520 (comment) Specifically, mutation errors will always bubble up, even when you're offline, as network errors. Queries and optimistic mutations are the ones that may silently fail in the background, optimistic mutations fail silently, because they will still be reflected in the UI and will be resent when online again, and queries because the However the
Offline is a bit of a "misnomer" here; I've often seen that The thing to understand here is how Specifically, it detects the kind of error (although it can also fall back to Now, the last thing is that you should not use the But let's get to actually talk about how you could implement an explicit timeout. You'd likely want to write an exchange that: (1) sets a limit on how long the import { Exchange, makeOperation } from '@urql/core';
import { pipe, share, mergeMap, merge, filter, fromValue, delay, takeUntil, onPush } from 'wonka';
export const timeoutExchange = ({ timeout = 1500 }): Exchange => ({ client, forward }) => {
return ops$ => {
const sharedOps$ = share(ops$);
const sharedResults$ = pipe(ops$, forward, share);
// For each operation we construct a "timeout limit"
const timeoutResults$ = pipe(
sharedOps$,
mergeMap((operation) => {
// This listens to teardowns, i.e. other, unrelated cancellations per operation
const teardown$ = pipe(
sharedOps$,
filter(op => op.kind === 'teardown' && op.key === operation.key)
);
// This listens to results per operation
const result$ = pipe(
sharedResults$,
filter(res => res.operation.key === operation.key)
);
return pipe(
// This value will be emitted unless some other preconditions are met (it's a timeout network error)
fromValue(makeErrorResult(
operation,
new Error('Network Error: Request timed out')
)),
// First we delay this result by the timeout
delay(timeout),
// Then we'll only take its value if it comes before a given cancellation or other result, as defined above
takeUntil(merge([teardown$, result$])),
// Otherwise we'll also issue a cancellation to the client ourselves, to stop the request
onPush(() => {
client.reexecuteOperation(makeOperation('teardown', operation))
})
);
})
);
// This means we let all results through, but we add on our timeouts' results
return merge([sharedResults$, timeoutResults$]);
};
}; I haven't really tested this thoroughly, but I left some comments to elaborate what it does; |
Beta Was this translation helpful? Give feedback.
There's a couple of things to unpack here and the
offlineExchange
's behaviour may also be tweaked in the future to accommodate to some of the use-cases we've now heard of or seen, but until then there's a couple of things to keep in mind.This thread had a couple of explanations on the specific error behaviour: #1520 (comment)
Specifically, mutation errors will always bubble up, even when you're offline, as network errors. Queries and optimistic mutations are the ones that may silently fail in the background, optimistic mutations fail silently, because they will still be reflected in the UI and will be resent when online…