Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/async-computed-supported.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@builder.io/qwik': patch
---

Async functions in `useComputed$` are no longer deprecated, since they now remain supported in Qwik v2.
2 changes: 1 addition & 1 deletion packages/docs/src/routes/api/qwik/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -3034,7 +3034,7 @@
}
],
"kind": "Function",
"content": "Returns a computed signal which is calculated from the given function. A computed signal is a signal which is calculated from other signals. When the signals change, the computed signal is recalculated, and if the result changed, all tasks which are tracking the signal will be re-run and all components that read the signal will be re-rendered.\n\nThe function must be synchronous and must not have any side effects.\n\nAsync functions are deprecated because:\n\n- When calculating the first time, it will see it's a promise and it will restart the render function. - Qwik can't track used signals after the first await, which leads to subtle bugs. - Both `useTask$` and `useResource$` are available, without these problems.\n\nIn v2, async functions won't work.\n\n\n```typescript\nuseComputed$: <T>(qrl: ComputedFn<T>) => Signal<Awaited<T>>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nqrl\n\n\n</td><td>\n\n[ComputedFn](#computedfn)<!-- -->&lt;T&gt;\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n\n**Returns:**\n\n[Signal](#signal)<!-- -->&lt;Awaited&lt;T&gt;&gt;",
"content": "Returns a computed signal which is calculated from the given function. A computed signal is a signal which is calculated from other signals. When the signals change, the computed signal is recalculated, and if the result changed, all tasks which are tracking the signal will be re-run and all components that read the signal will be re-rendered.\n\nThe function must not have any side effects. It may return a promise, but in Qwik v1 only signal reads before the first await are tracked. Read reactive inputs before awaiting.\n\n\n```typescript\nuseComputed$: <T>(qrl: ComputedFn<T>) => Signal<Awaited<T>>\n```\n\n\n<table><thead><tr><th>\n\nParameter\n\n\n</th><th>\n\nType\n\n\n</th><th>\n\nDescription\n\n\n</th></tr></thead>\n<tbody><tr><td>\n\nqrl\n\n\n</td><td>\n\n[ComputedFn](#computedfn)<!-- -->&lt;T&gt;\n\n\n</td><td>\n\n\n</td></tr>\n</tbody></table>\n\n**Returns:**\n\n[Signal](#signal)<!-- -->&lt;Awaited&lt;T&gt;&gt;",
"editUrl": "https://github.com/QwikDev/qwik/tree/main/packages/qwik/src/core/use/use-task.ts",
"mdFile": "qwik.usecomputed_.md"
},
Expand Down
8 changes: 1 addition & 7 deletions packages/docs/src/routes/api/qwik/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10182,13 +10182,7 @@ T

Returns a computed signal which is calculated from the given function. A computed signal is a signal which is calculated from other signals. When the signals change, the computed signal is recalculated, and if the result changed, all tasks which are tracking the signal will be re-run and all components that read the signal will be re-rendered.

The function must be synchronous and must not have any side effects.

Async functions are deprecated because:

- When calculating the first time, it will see it's a promise and it will restart the render function. - Qwik can't track used signals after the first await, which leads to subtle bugs. - Both `useTask$` and `useResource$` are available, without these problems.

In v2, async functions won't work.
The function must not have any side effects. It may return a promise, but in Qwik v1 only signal reads before the first await are tracked. Read reactive inputs before awaiting.

```typescript
useComputed$: <T>(qrl: ComputedFn<T>) => Signal<Awaited<T>>;
Expand Down
10 changes: 5 additions & 5 deletions packages/docs/src/routes/docs/(qwik)/core/state/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -256,16 +256,16 @@ Incrementing the counter updates the `count` signal, but `componentId` keeps its

In Qwik, there are two ways to create computed values, each with a different use case (in order of preference):

1. `useComputed$()`: `useComputed$()` is the preferred way of creating computed values. Use it when the computed value can be derived synchronously purely from the source state (current application state). For example, creating a lowercase version of a string or combining first and last names into a full name.
1. `useComputed$()`: `useComputed$()` is the preferred way of creating computed values. Use it when the computed value can be derived from the source state (current application state), either synchronously or asynchronously. For example, creating a lowercase version of a string or combining first and last names into a full name.

2. [`useResource$()`](/docs/(qwik)/core/state/index.mdx#useresource): `useResource$()` is used when the computed value is asynchronous or the state comes from outside of the application. For example, fetching the current weather (external state) based on a current location (application internal state).
2. [`useResource$()`](/docs/(qwik)/core/state/index.mdx#useresource): `useResource$()` is used when the state comes from outside of the application or you need explicit loading and error states. For example, fetching the current weather (external state) based on a current location (application internal state).


In addition to the two ways of creating computed values described above, there is also a lower-level ([`useTask$()`](/docs/(qwik)/core/tasks/index.mdx#usetask)). This way does not produce a new signal, but rather modifies the existing state or produces a side effect.

### `useComputed$()`

Use `useComputed$` to memoize a value derived synchronously from other state.
Use `useComputed$` to memoize a value derived synchronously or asynchronously from other state.

It is similar to `memo` in other frameworks, since it will only recompute the value when one of the input signals changes.

Expand All @@ -291,11 +291,11 @@ export default component$(() => {
```
</CodeSandbox>

> **NOTE** Because `useComputed$()` is synchronous it is not necessary to explicitly track the input signals.
> **NOTE** Signal reads in `useComputed$()` are tracked automatically. In an async computation, only reads before the first `await` are tracked, so read reactive inputs before awaiting.

### `useResource$()`

Use `useResource$()` to create a computed value that is derived asynchronously. It's the asynchronous version of `useComputed$()`, which includes the `state` of the resource (loading, resolved, rejected) on top of the value.
Use `useResource$()` to create a value from an external async source when you need the `state` of the resource (loading, resolved, rejected) in addition to the value.

A common use of `useResource$()` is to fetch data from an external API within the component, which can occur either on the server or the client.

Expand Down
33 changes: 5 additions & 28 deletions packages/qwik/src/core/use/use-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ import {
type SignalInternal,
} from '../state/signal';
import { implicit$FirstArg } from '../util/implicit_dollar';
import { logError, logErrorAndStop, logOnceWarn } from '../util/log';
import { logError, logErrorAndStop } from '../util/log';
import { ComputedEvent, TaskEvent } from '../util/markers';
import { delay, isPromise, maybeThen, safeCall } from '../util/promises';
import { delay, maybeThen, safeCall } from '../util/promises';
import { isFunction, isObject, type ValueOrPromise } from '../util/types';
import { invoke, newInvokeContext, untrack, useInvokeContext, waitAndRun } from './use-core';
import { useOn, useOnDocument } from './use-on';
Expand Down Expand Up @@ -337,16 +337,8 @@ export const useComputedQrl = <T>(qrl: QRL<ComputedFn<T>>): Signal<Awaited<T>> =
* recalculated, and if the result changed, all tasks which are tracking the signal will be re-run
* and all components that read the signal will be re-rendered.
*
* The function must be synchronous and must not have any side effects.
*
* Async functions are deprecated because:
*
* - When calculating the first time, it will see it's a promise and it will restart the render
* function.
* - Qwik can't track used signals after the first await, which leads to subtle bugs.
* - Both `useTask$` and `useResource$` are available, without these problems.
*
* In v2, async functions won't work.
* The function must not have any side effects. It may return a promise, but in Qwik v1 only signal
* reads before the first await are tracked. Read reactive inputs before awaiting.
*
* @public
*/
Expand Down Expand Up @@ -761,22 +753,7 @@ export const runComputed = (
};
try {
return maybeThen(task.$qrl$.$resolveLazy$(containerState.$containerEl$), () => {
const result = taskFn();
if (isPromise(result)) {
const warningMessage =
'useComputed$: Async functions in computed tasks are deprecated and will stop working in v2. Use useTask$ or useResource$ instead.';
const stack = new Error(warningMessage).stack;
if (!stack) {
logOnceWarn(warningMessage);
} else {
const lessScaryStack = stack.replace(/^Error:\s*/, '');
logOnceWarn(lessScaryStack);
}

return result.then(ok, fail);
} else {
ok(result);
}
return safeCall(taskFn, ok, fail);
});
} catch (reason) {
fail(reason);
Expand Down
Loading