У меня есть файл свойств json в папке assets/constants/props.json
. В файле json у меня есть одно имя ключа someValue
и его значение abc
.
JSON-файл выглядит так:
У меня есть служебный файл constants.service.ts
и в нем 2 метода:
public getJson(url : string) : Observable<any> {
return this.http.get(url);
}
public someValue() : string {
return this.getJson("assets/constants/props.json").subscribe(async (data: any) => {
return await data.someValue;
});
}
А также в одном из своих компонентов я использую это значение:
private someValue! : string;
ngOnInit() {
this.someValue = this.server.someValue();
}
Таким образом я получаю: Type 'Subscription' is not assignable to type 'string'.
ошибка.
Я пробовал много способов, и ни один из них не работал:
метод make someValue()
возвращает Promise<string>
сделать ngOnInit()
асинхронный метод и использовать await
в нем
используя unsubsribe
, toPromise()
, toString()
...
И многое другое...
Каждое решение выдавало мне другое сообщение об ошибке или неправильное значение [object object]
вместо «abc».
Я перепробовал все возможные варианты, и я не могу получить ожидаемый результат.
Мой вопрос в том, как правильно использовать observables
, async
, await
, subscribe
Вы не можете напрямую преобразовать Observable<string>
или Promise<string>
в string
, потому что это не имеет семантического смысла.
И Observable, и Promises представляют значения, которые пока недоступны, но могут быть предоставлены в какой-то момент в будущем. Поскольку все API-интерфейсы ввода-вывода JavaScript, о которых я знаю, спроектированы неблокирующими, вы не можете позволить своему процессу бездействовать, ждать и ничего не делать (т. е. блокировать), пока не завершится операция ввода-вывода.
Вместо этого вы регистрируете функцию обратного вызова, которая будет вызываться в более поздний момент времени, когда значение будет доступно. Вот для чего нужны subscribe
в RxJS или then
в Promises.
Ключевые слова async
и await
— это просто синтаксический сахар для создания промисов и регистрации на них обратных вызовов соответственно. Под капотом async он просто преобразует код, который следует за ним, в функцию обратного вызова и регистрирует его в промисе. Так что в вашем примере он вообще ничего полезного не делает.
Есть несколько способов получить значение из Observable.
Вот самое простое:
ngOnOnit() {
this.server.someValue().subscribe(value => this.someValue = value);
}
Но это немного громоздко, и с этим есть некоторые предостережения, такие как потенциальные утечки памяти.
Есть лучший способ: просто позвольте Angular разобраться с этим, используя AsyncPipe. Просто отправьте Observable
прямо в шаблон и дайте Angular подписаться на него:
someValue$: Observable<string>; // The "$" sign is just naming convention to tell Observables and plain values apart
ngOnInit() {
this.someValue$ = this.server.someValue();
}
<p>Your value: {{someValue$ | async}}</p>
В качестве примечания: мы надеемся, что все это станет намного проще с Angular 16, когда он получит поддержку Signals.
await работает над Promise, а Observable не является Promise. Вы можете преобразовать Observable в Promise, например. используя lastValueFrom, а затем ждите его.
И, конечно, значение недоступно сразу, потому что вы имеете дело с асинхронным вводом-выводом. Это то, что я пытался объяснить все это время, я, честно говоря, не знаю, как сделать это яснее.
Также я по-прежнему рекомендую AsyncPipe вместо lastValueFrom + await.
но если я использую вашу идею
this.server.someValue().subscribe(value => this.someValue = value);
и пишуalert(this.someValue)
после предыдущей строки, я получаюundefined
, если я не жду несколько миллисекунд, и тогда это сработает. Как я могу дождаться фактической загрузки значения, а затем сделать предупреждение. Почемуawait
не работает?