Hata Ansiklopedisi

Resource bir değer üretmeden tamamlandı

Bu hata, Observable tabanlı bir resource (rxResource veya httpResource) hiçbir değer ya da hata göndermeden sona erdiğinde (tamamlandığında) oluşur.

Bir resource her zaman bir şeyle sonuçlanmalıdır: yüklediği bir değer veya neyin yanlış gittiğini açıklayan bir hata. Sessizce tamamlanan bir Observable, Angular'a bunların hiçbirini vermez; bu yüzden Angular resource'a ne koyacağını bilemez ve bunun yerine bu hatayı fırlatır.

httpResource ile

httpResource size kendiniz yazacağınız bir Observable vermez; HTTP çağrısını dahili olarak HttpClient üzerinden yapar ve yanıt geldiğinde tamamlanır. Dolayısıyla bu hatayı httpResource'tan görüyorsanız sorun isteğin kendisinde değildir; önünde duran bir şey yanıtı resource'a ulaşmadan yutuyordur.

Olağan şüpheli, bir hatayı yakalayıp hiçbir şey yeniden yaymayan bir HttpInterceptor'dır. Bu, aşağıdaki EMPTY hatasının aynısıdır, ancak kendi kodunuz yerine bir interceptor'da bulunur:

@Injectable()
class MyInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<unknown>, next: HttpHandler) {
    return next.handle(req).pipe(
      // KÖTÜ: bir `stream` içindeki `catchError(() => EMPTY)` ile aynı sorun;
      // istek yanıt ve hata olmadan tamamlanır ve ona bağlı
      // her httpResource NG0991 hatasıyla karşılaşır.
      catchError(() => EMPTY),
    );
  }
}

Bunu, genel olarak bir interceptor pipeline'ında yapacağınız şekilde düzeltin: hatanın yayılmasına izin verin (catchError'ı kaldırın) veya onu isteği gerçekten sonuçlandıran bir şeyle değiştirin, örneğin of(new HttpResponse({body: fallbackValue})).

rxResource ile

Buna yanlışlıkla neden olmanın en yaygın yolu, bir hatayı yakalayıp EMPTY ile yutmaktır:

import {rxResource} from '@angular/core/rxjs-interop';
import {EMPTY, catchError} from 'rxjs';

const userResource = rxResource({
  params: () => ({id: userId()}),
  stream: ({params}) =>
    this.http.get(`/users/${params.id}`).pipe(
      // KÖTÜ: bu, hatayı tamamen gizler. Observable hiçbir şey
      // olmadan tamamlanır ve resource'un gösterecek ne bir değeri ne de
      // bir hatası olur; NG0991'i tetikleyen budur.
      catchError(() => EMPTY),
    ),
});

Bunun yaygın olarak gerçekleştiği bir başka yol: birden fazla Observable'ı (merge, race ve benzerleriyle) birleştirirken, bunlardan birinin hiç yaymadan kendi kendine tamamlanabilmesi ve başlangıç değerini garanti edecek bir startWith(...) bulunmaması. Birleştirilen kaynakların hiçbiri, hepsi tamamlanmadan önce yayım yapmazsa, birleşik akış da boş olarak tamamlanır.

Durumunuza uygun düzeltmeyi seçin:

Hatanın geçmesine izin verin. Hatayı kendiniz ele almanız gerekmiyorsa, catchError'ı kaldırmanız yeterlidir; resource error durumuna geçer ve bunu zaten userResource.error() ile kontrol edebilirsiniz.

stream: ({params}) => this.http.get(`/users/${params.id}`),

Yedek bir değerle kurtarın. Hata yerine bir şey göstermeyi tercih ediyorsanız, of(...) gibi bir değer yayan bir Observable döndürmek için catchError kullanın:

import {of, catchError} from 'rxjs';

stream: ({params}) =>
  this.http.get(`/users/${params.id}`).pipe(
    catchError(() => of(null)), // resource hata vermek yerine `null` ile sonuçlanır
  ),

Her iki durumda da, stream'den döndürdüğünüz Observable'ın tamamlanmadan önce her zaman en az bir değer veya bir hata yaydığından emin olun.

Bu hatayı gerçekte nerede göreceksiniz

Hata, resource oluşturulduğunda fırlatılmaz ve isteğin tamamlandığı yerde de fırlatılması gerekmez. Bir şey resource'un değerini bir sonraki okuduğunda fırlatılır; örneğin bir şablon bağlamasındaki userResource.value(). Bu, asıl neden daha yukarıda olduğu halde (boş tamamlanan stream veya yanıtı yutan bir interceptor) hatanın uygulamanızın başka bir yerinde olduğu izlenimini verebilir.

Kodunuzdaki hiçbir şey .value()'yu okumadan önce resource'un durumunu kontrol etmiyorsa, bu hata, bir şablon render edilirken fırlatılan diğer yakalanmamış hatalar gibi Angular'ın global ErrorHandler'ına kadar ulaşabilir.

Bunun etkisini azaltmanın en çok tercih edilenden en az tercih edilene doğru iki yolu:

  1. Sorunu kökünden çözün; yukarıdaki yaklaşımlardan birini kullanarak resource'un hiçbir zaman bu duruma düşmemesini sağlayın.
  2. .value() okumalarını koruma altına alın; bu, tam olarak kontrol etmediğiniz bir şeyin (örneğin üçüncü taraf bir interceptor veya servis) beklenmedik şekilde boş tamamlanması ihtimaline karşı ek bir savunma katmanıdır. .value()'yu koşulsuz okumak yerine, okumadan önce .hasValue() veya .status()'u kontrol edin; örneğin @if (userResource.hasValue()) { {{ userResource.value() }} } ile.