Is it possible to use async await inside of an Angular computed signal?
I have tried to play with that but so far i couldn't make it work correctrly (fetching the value, manipulate the value and returning a raw value to the signal computed result and not a promise)
Use resources instead.
Use resource (for Promises) or rxResource (for Observables) instead.
Update: added generalization of the concept
Update2: Added cancellation to observable variant
This is a very important question. As @kemsky already mentioned you can use an effect to create such signals.
The nicest solution is to encapsulate the creation and management of such signal in a function, like this:
function myBlahSignal(otherSignal: Signal<Something>) {
const someService = inject(SomeService);
const resultSignal = signal<SomeResult>(null);
effect(
() => {
if (!otherSignal()) return;
someService
.someMethod(otherSignal())
.subscribe((result) => resultSignal.set(result));
},
{ allowSignalWrites: true }
);
return resultSignal.asReadonly();
}
You can then use such function directly in any component that has the required source signals:
export class MyComponent {
otherSignal = someOtherSignal();
myBlah = myBlahSignal(this.otherSignal1);
}
If we look at this pattern fundamentally, we see that we basically implemented a async computed signal, based on observables. We can abstract this concept in a reusable way:
function myBlahSignal(otherSignal: Signal<Something>) {
const someService = inject(SomeService);
return asyncObservableComputed(() => {
if (!otherSignal()) return null;
return someService.someMethod$(otherSignal());
});
}
function asyncObservableComputed<T>(
computation: () => Observable<T> | undefined | null,
): Signal<T> {
const resultSignal = signal<T>(null);
effect(
(onCleanup) => {
const result$ = computation();
if (!result$) return;
const subscription = result$.subscribe((result) => resultSignal.set(result));
onCleanup(() => subscription.unsubscribe());
},
{ allowSignalWrites: true },
);
return resultSignal.asReadonly();
}
And a promise based variant:
function asyncPromiseComputed<T>(
computation: () => Promise<T> | undefined | null,
): Signal<T> {
const resultSignal = signal<T>(null);
effect(
async () => {
const result = await computation();
resultSignal.set(result);
},
{ allowSignalWrites: true },
);
return resultSignal.asReadonly();
}
We can go one step further and make a fully generic asyncComputed that supports Observable, Promise and plain results at the same time:
export function asyncComputed<T>(
computation: () => Observable<T> | Promise<T> | T | undefined | null,
): Signal<T> {
const resultSignal = signal<T>(null);
effect(
async () => {
const result = computation();
const unwrappedResult = await (isObservable(result)
? firstValueFrom(result as Observable<T>, { defaultValue: null })
: result);
resultSignal.set(unwrappedResult);
},
{ allowSignalWrites: true },
);
return resultSignal.asReadonly();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With