mannaxxx 1
mannaxxx
Bvural41 1
Bvural41
Agora Metin2 1
Agora Metin2
berkmenoo 1
berkmenoo
farkmt2official 1
farkmt2official
kralhakan2009 1
kralhakan2009
Vahsi Uzman 1
Vahsi Uzman
Hikaye Ekle

[NODE JS] Promise.allSettled ile Tum Sonuclari Alma

nodejs.png


[NODE JS] Promise.allSettled ile Tum Sonuclari Alma​


Merhaba arkadaslar, bir onceki konuda Promise.all'in fail-fast davranis sergiledigini soylemistim. Yani bir Promise bile reject olsa tum islem iptal oluyordu. Peki ya her Promise'in sonucunu, basarili olsun ya da olmasin, almak isterseniz ne olacak? Iste Promise.allSettled tam bu is icin tasarlanmis. ES2020 ile gelen bu fonksiyon, production'da cok ise yariyor.

allSettled vs all​


Promise.allSettled, tum Promise'lerin sonuclanmasini (resolve veya reject farketmez) bekler ve her birinin sonucunu ayri ayri raporlar. Donen dizideki her eleman bir nesne: ya { status: 'fulfilled', value: sonuc } ya da { status: 'rejected', reason: hata }. Bu sayede hangi islemlerin basarili hangilerinin basarisiz oldugunu tek tek gorebilirsiniz.

Ben bu fonksiyonu ozellikle toplu islemlerde kullaniyorum. Mesela 100 kullaniciya bildirim gondermem gerekiyor. Promise.all kullansam, bir kullanicinin bildirimi basarisiz olsa geri kalan 99'unun sonucunu goremem. allSettled ile hepsinin sonucunu alip, basarisiz olanlari ayri ele alabilirim. Gercek hayatta tam ihtiyac bu.

JavaScript:
// Promise.allSettled temel kullanim
async function temelOrnek() {
    const gorevler = [
        Promise.resolve('Basarili 1'),
        Promise.reject(new Error('Hata 1')),
        Promise.resolve('Basarili 2'),
        Promise.reject(new Error('Hata 2')),
        Promise.resolve('Basarili 3')
    ];

    const sonuclar = await Promise.allSettled(gorevler);

    console.log('=== Tum Sonuclar ===');
    sonuclar.forEach((sonuc, index) => {
        if (sonuc.status === 'fulfilled') {
            console.log(`  Gorev ${index + 1}: BASARILI - ${sonuc.value}`);
        } else {
            console.log(`  Gorev ${index + 1}: BASARISIZ - ${sonuc.reason.message}`);
        }
    });

    // Basarili ve basarisiz olanlari ayir
    const basarililar = sonuclar.filter(s => s.status === 'fulfilled');
    const basarisizlar = sonuclar.filter(s => s.status === 'rejected');

    console.log(`\nBasarili: ${basarililar.length}, Basarisiz: ${basarisizlar.length}`);
}

temelOrnek();

// Gercek hayat ornegi: Toplu bildirim gonderimi
async function topluBildirimGonder(kullanicilar, mesaj) {
    const bildirimIslemleri = kullanicilar.map(kullanici =>
        bildirimGonder(kullanici, mesaj)
    );

    const sonuclar = await Promise.allSettled(bildirimIslemleri);

    const rapor = {
        toplam: sonuclar.length,
        basarili: 0,
        basarisiz: 0,
        hatalar: []
    };

    sonuclar.forEach((sonuc, index) => {
        if (sonuc.status === 'fulfilled') {
            rapor.basarili++;
        } else {
            rapor.basarisiz++;
            rapor.hatalar.push({
                kullanici: kullanicilar[index].ad,
                hata: sonuc.reason.message
            });
        }
    });

    console.log(`\n=== Bildirim Raporu ===`);
    console.log(`Toplam: ${rapor.toplam}`);
    console.log(`Basarili: ${rapor.basarili}`);
    console.log(`Basarisiz: ${rapor.basarisiz}`);

    if (rapor.hatalar.length > 0) {
        console.log('\nHatalar:');
        rapor.hatalar.forEach(h => {
            console.log(`  - ${h.kullanici}: ${h.hata}`);
        });
    }

    return rapor;
}

// Bildirim gonderme simulasyonu
function bildirimGonder(kullanici, mesaj) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // Rastgele basarisizlik simule et
            if (Math.random() < 0.3) {
                reject(new Error(`${kullanici.ad} icin gonderim basarisiz`));
            } else {
                resolve({ kullaniciId: kullanici.id, durum: 'gonderildi' });
            }
        }, Math.random() * 200);
    });
}

// Kullanim
const kullaniciListesi = [
    { id: 1, ad: 'Ahmet' },
    { id: 2, ad: 'Ayse' },
    { id: 3, ad: 'Mehmet' },
    { id: 4, ad: 'Fatma' },
    { id: 5, ad: 'Ali' }
];

topluBildirimGonder(kullaniciListesi, 'Merhaba!');

Retry Mekanizmasi ile Birlikte Kullanim​


allSettled'in guclu bir kullanim alani da retry mekanizmasiyla birlikte calismaktir. Ilk turda basarisiz olanlari tespit edip, sadece onlari tekrar deneyebilirsiniz. Bu yaklasim ozellikle dis servislere yapilan toplu API cagrilarinda cok ise yarar.

JavaScript:
// Retry mekanizmali toplu islem
async function retryliTopluIslem(islemler, maxDeneme = 3) {
    let kalanIslemler = islemler.map((islem, index) => ({ index, islem }));
    const tumSonuclar = new Array(islemler.length);
    let deneme = 0;

    while (kalanIslemler.length > 0 && deneme < maxDeneme) {
        deneme++;
        console.log(`\nDeneme ${deneme}/${maxDeneme} - ${kalanIslemler.length} islem...`);

        const sonuclar = await Promise.allSettled(
            kalanIslemler.map(item => item.islem())
        );

        const yeniKalan = [];

        sonuclar.forEach((sonuc, i) => {
            const orijinalIndex = kalanIslemler[i].index;

            if (sonuc.status === 'fulfilled') {
                tumSonuclar[orijinalIndex] = { basarili: true, deger: sonuc.value, deneme };
            } else if (deneme < maxDeneme) {
                yeniKalan.push(kalanIslemler[i]);
            } else {
                tumSonuclar[orijinalIndex] = { basarili: false, hata: sonuc.reason.message, deneme };
            }
        });

        kalanIslemler = yeniKalan;
    }

    return tumSonuclar;
}

// Kullanim
async function main() {
    let cagriSayisi = 0;

    const islemler = Array.from({ length: 10 }, (_, i) => {
        return () => new Promise((resolve, reject) => {
            cagriSayisi++;
            setTimeout(() => {
                // Her denemede basari sansi artiyor
                if (Math.random() < 0.6) {
                    resolve(`Islem ${i + 1} basarili`);
                } else {
                    reject(new Error(`Islem ${i + 1} basarisiz`));
                }
            }, 50);
        });
    });

    const sonuclar = await retryliTopluIslem(islemler, 3);

    console.log('\n=== Final Rapor ===');
    sonuclar.forEach((s, i) => {
        const durum = s.basarili ? 'OK' : 'FAIL';
        console.log(`  Islem ${i + 1}: ${durum} (deneme: ${s.deneme})`);
    });

    console.log(`\nToplam API cagrisi: ${cagriSayisi}`);
}

main().catch(console.error);

Ne Zaman allSettled, Ne Zaman all?​


Tecrubeme gore su duruma gore karar verin: Eger tum islemlerin basarili olmasi zorunluysa (ornegin bir transaction'in tum adimlari) Promise.all kullanin. Eger islemler birbirinden bagimsiz ve kismini basarisizlik kabul edilebilirse Promise.allSettled kullanin. Toplu e-posta gonderimi, coklu API veri toplama, health check gibi islemlerde allSettled daha mantikli. Odeme islemi, veritabani migration gibi islemlerde all kullanin.

allSettled olmadan once insanlar benzer davranisi elde etmek icin her Promise'i catch ile sarip hatayı degere donusturuyorlardi. Ama bu hem cikkin hem de error bilgisini kaybetme riski tasiyordu. allSettled bu sorunu kokeninden cozdü. Hadi sonraki konuya gecelim, Promise.race sizi bekliyor!
 

Şu an konuyu görüntüleyenler (Toplam : 0, Üye: 0, Misafir: 0)

Geri
Üst