async / await ile blokları yakala / dene


116

Düğüm 7 eşzamansız / bekle özelliğini kazıyorum ve böyle bir kodla karşılaşmaya devam ediyorum

function getQuote() {
  let quote = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
  return quote;
}

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}

main();

Bu, async / await ile çözme / reddetme veya geri dönme / atma olasılığı gibi görünüyor, ancak v8, try / catch blokları içindeki kodu optimize etmiyor mu ?!

Alternatifler var mı?


'Bekledikten sonra atmak başarılı değil' ne anlama geliyor? Hatalar varsa? Beklenen sonucu döndürmezse? Yakalama bloğunu yeniden atabilirsin.
DevDig

afaik v8 dene / yakala optimize ediyor, fırlatma ifadesi yavaş olanı
Tamas Hegedus

1
Hâlâ soruyu anlamıyorum. Eski söz zincirini kullanıyorsunuz ama daha hızlı olacağını sanmıyorum. Yani, deneme yakalama performansıyla ilgili endişeleriniz mi var? O zaman async await ile ne yapmalı?
Tamas Hegedus

Cevabımı kontrol et Daha temiz bir yaklaşım elde etmeye çalıştım
zardilior

Burada bunu yapabilirsiniz stackoverflow.com/a/61833084/6482248 Daha temiz görünüyor
Prathamesh More

Yanıtlar:


133

Alternatifler

Buna bir alternatif:

async function main() {
  try {
    var quote = await getQuote();
    console.log(quote);
  } catch (error) {
    console.error(error);
  }
}

açıkça vaatleri kullanarak böyle bir şey olurdu:

function main() {
  getQuote().then((quote) => {
    console.log(quote);
  }).catch((error) => {
    console.error(error);
  });
}

veya bunun gibi, devam eden geçiş stilini kullanarak:

function main() {
  getQuote((error, quote) => {
    if (error) {
      console.error(error);
    } else {
      console.log(quote);
    }
  });
}

Orijinal örnek

Orijinal kodunuzun yaptığı, yürütmeyi askıya almak ve tarafından verilen sözün yerine getirilmesini beklemektir getQuote(). Daha sonra yürütmeye devam eder ve döndürülen değeri üzerine yazar var quoteve ardından söz çözülmüşse bunu yazdırır veya bir istisna atar ve söz reddedilmişse hatayı yazdıran catch bloğunu çalıştırır.

İkinci örnekte olduğu gibi doğrudan Promise API'yi kullanarak aynı şeyi yapabilirsiniz.

Verim

Şimdi, performans için. Hadi test edelim!

Bu kodu az önce yazdım - bir dönüş değeri olarak f1()verir 1, bir istisna olarak f2()atar 1:

function f1() {
  return 1;
}

function f2() {
  throw 1;
}

Şimdi aynı kodu milyon kez arayalım, ilk olarak f1():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f1();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);

Sonra 's değişikliği izin f1()için f2():

var sum = 0;
for (var i = 0; i < 1e6; i++) {
  try {
    sum += f2();
  } catch (e) {
    sum += e;
  }
}
console.log(sum);

Aldığım sonuç bu f1:

$ time node throw-test.js 
1000000

real    0m0.073s
user    0m0.070s
sys     0m0.004s

Bunun için aldım f2:

$ time node throw-test.js 
1000000

real    0m0.632s
user    0m0.629s
sys     0m0.004s

Tek iş parçacıklı bir işlemde saniyede 2 milyon atış gibi bir şey yapabileceğiniz anlaşılıyor. Bundan fazlasını yapıyorsanız, endişelenmeniz gerekebilir.

özet

Node'da böyle şeyler için endişelenmem. Böyle şeyler çok kullanılırsa, sonunda V8 veya SpiderMonkey veya Chakra ekipleri tarafından optimize edilecek ve herkes takip edecek - bu prensip olarak optimize edilmemiş gibi değil, sadece sorun değil.

Optimize edilmemiş olsa bile, CPU'nuzu Node'da maksimize ediyorsanız, muhtemelen sayınızı C ile yazmanız gerektiğini savunabilirim - diğer şeylerin yanı sıra yerel eklentiler bunun içindir. Ya da belki node.native gibi şeyler iş için Node.js'den daha uygun olabilir.

Bu kadar çok istisnanın atılması gereken bir kullanım durumunun ne olacağını merak ediyorum. Genellikle bir değer döndürmek yerine bir istisna atmak bir istisnadır.


Kodun Promises ile kolayca yazılabileceğini biliyorum, daha önce de belirtildiği gibi, çeşitli örneklerde gördüm, bu yüzden soruyorum. Try / catch içinde tek bir işlemin olması bir sorun olmayabilir, ancak daha fazla uygulama mantığına sahip birden çok zaman uyumsuz / bekleme işlevi olabilir.
Patrick

4
@Patrick "olabilir" ve "olacak", spekülasyon ile test etme arasındaki farktır. Tek bir ifade için test ettim çünkü sorunuzda olan buydu ancak örneklerimi birden fazla ifadeyi test etmek için kolayca dönüştürebilirsiniz. Ayrıca sizin de sorduğunuz eşzamansız kod yazmak için birkaç seçenek daha sundum. Sorunuzu cevaplarsa , cevabı kabul etmeyi düşünebilirsiniz . Özetlemek gerekirse: Elbette istisnalar geri dönüşlerden daha yavaştır, ancak kullanımları bir istisna olmalıdır.
rsp

1
Bir istisna yapmanın gerçekten de bir istisna olması gerekiyor. Bununla birlikte, bir istisna atsanız da atmasanız da kod optimize edilmemiştir. Performans vuruşu try catch, bir istisna atmaktan değil, kullanmaktan gelir . Rakamlar küçük olsa da testlerinize göre neredeyse 10 kat daha yavaş ki bu da önemsiz değil.
Nepoxx

22

Golang'da Hata İşlemeye Benzer Alternatif

Async / await, başlık altında sözler kullandığından, aşağıdaki gibi küçük bir yardımcı program işlevi yazabilirsiniz:

export function catchEm(promise) {
  return promise.then(data => [null, data])
    .catch(err => [err]);
}

Ardından, bazı hataları yakalamanız gerektiğinde bunu içe aktarın ve onunla bir söz veren zaman uyumsuz işlevinizi sarın.

import catchEm from 'utility';

async performAsyncWork() {
  const [err, data] = await catchEm(asyncFunction(arg1, arg2));
  if (err) {
    // handle errors
  } else {
    // use data
  }
}

Tam olarak yukarıdakileri yapan bir NPM paketi oluşturdum - npmjs.com/package/@simmo/task
Mike

2
@Mike Tekerleği yeniden icat ediyor olabilirsiniz - tam olarak bunu yapan popüler bir paket zaten var: npmjs.com/package/await-to-js
Jakub

21

Try-catch bloğuna bir alternatif, await-to-js lib'dir. Ben sık sık kullanırım. Örneğin:

import to from 'await-to-js';

async function main(callback) {
    const [err,quote] = await to(getQuote());
    
    if(err || !quote) return callback(new Error('No Quote found'));

    callback(null,quote);

}

Bu sözdizimi, try-catch ile karşılaştırıldığında çok daha temizdir.


Bunu denedim ve sevdim. Yeni bir modül takma pahasına temiz ve okunabilir kod. Ancak çok sayıda eşzamansız işlev yazmayı planlıyorsanız, bunun harika bir eklenti olduğunu söylemeliyim! Teşekkürler
filipbarak

15
async function main() {
  var getQuoteError
  var quote = await getQuote().catch(err => { getQuoteError = err }

  if (getQuoteError) return console.error(err)

  console.log(quote)
}

Alternatif olarak, olası bir değişkeni en üstte bir hata tutmak için bildirmek yerine yapabilirsiniz

if (quote instanceof Error) {
  // ...
}

TypeError veya Reference hatası gibi bir şey atılırsa bu işe yaramaz. Bununla birlikte, bunun normal bir hata olduğundan emin olabilirsiniz.

async function main() {
  var quote = await getQuote().catch(err => {
    console.error(err)      

    return new Error('Error getting quote')
  })

  if (quote instanceOf Error) return quote // get out of here or do whatever

  console.log(quote)
}

Bunun için tercihim, her şeyi büyük bir deneme bloğunda sarmalamak, birden fazla sözün yaratıldığı yerde, hatayı özellikle onu oluşturan söze göre ele almayı zahmetli hale getirebilir. Alternatif olarak, aynı derecede hantal bulduğum çoklu deneme blokları


8

Daha temiz bir alternatif şudur:

Her zaman uyumsuz işlevin teknik olarak bir vaat olması nedeniyle

Await ile çağırırken işlevlere yakalama ekleyebilirsiniz

async function a(){
    let error;

    // log the error on the parent
    await b().catch((err)=>console.log('b.failed'))

    // change an error variable
    await c().catch((err)=>{error=true; console.log(err)})

    // return whatever you want
    return error ? d() : null;
}
a().catch(()=>console.log('main program failed'))

Tüm vaat edilen hatalar ele alındığı ve kod hatalarınız olmadığı için, try catch'e gerek yok, bunu ebeveynde ihmal edebilirsiniz !!

Mongodb ile çalıştığınızı varsayalım, eğer bir hata varsa onu çağıran işlevde sarmalayıcılar yapmaktan veya try yakalamalarını kullanmaktan daha çok ilgilenmeyi tercih edebilirsiniz.


3 fonksiyonunuz var. Biri değerleri alıyor ve hatayı yakalıyor, hata yoksa bir diğeri geri dönüyorsunuz ve son olarak ilk işlevin bir hata döndürüp döndürmediğini kontrol etmek için bir geri arama ile bir çağrı. Bütün bunlar tek bir "promise" .sonra (cb) .catch (cb) veya trycatch bloğu ile çözülür.
Chief koshi

@Chiefkoshi Gördüğünüz gibi, her üç durumda da hata farklı ele alındığı için tek bir yakalama işe yaramaz. İlki başarısız olursa, d () döndürür, ikincisi başarısız olursa, sonuncusu başarısız olursa, farklı bir hata mesajı gösterilir. Soru, await kullanırken hataların işlenmesini ister. Yani cevap da budur. Herhangi biri başarısız olursa hepsi yürütülmelidir. Deneme yakalama blokları, bu örnekte daha temiz olmayan üç tane gerektirir
zardilior

1
Soru, başarısız vaatlerin ardından yerine getirilmesini istemiyor. Burada B'yi beklersiniz, sonra C'yi çalıştırır ve hata verirlerse D'yi döndürürsünüz. Bu nasıl daha temiz? C, B'yi beklemek zorundadır, ancak birbirlerinden bağımsızdırlar. Bağımsız iseler neden A'da birlikte olacakları için bir neden göremiyorum. Birbirlerine bağımlı olsalar, B başarısız olursa C'nin yürütülmesini durdurmak istersiniz, .then.catch veya try-catch işi. Hiçbir şey döndürmediklerini ve A ile tamamen ilgisiz bazı eşzamansız eylemler gerçekleştirdiklerini varsayıyorum. Neden eşzamansız bekleme ile çağrılıyorlar?
Chief koshi

Async / await kullanırken hataları işlemek için yakalama bloklarını denemeye yönelik alternatiflerle ilgili soru. Buradaki örnek açıklayıcı olmaktır ve bir örnekten başka bir şey değildir. Bağımsız işlemlerin, genellikle zaman uyumsuz / beklemenin nasıl kullanıldığı sıralı bir şekilde tek tek ele alınmasını gösterir. Neden eşzamansız bekleme ile çağrılıyorlar, sadece nasıl ele alınabileceğini göstermek için. Haklı olmaktan çok açıklayıcı.
zardilior

2

Bu şekilde yapmak istiyorum :)

const sthError = () => Promise.reject('sth error');

const test = opts => {
  return (async () => {

    // do sth
    await sthError();
    return 'ok';

  })().catch(err => {
    console.error(err); // error will be catched there 
  });
};

test().then(ret => {
  console.log(ret);
});

Hata işlemeye benzer co

const test = opts => {
  return co(function*() {

    // do sth
    yield sthError();
    return 'ok';

  }).catch(err => {
    console.error(err);
  });
};

Kod çok net değil adamım, yine de ilginç görünüyor, düzenleyebilir misin?
zardilior

Bu yanıtta hiçbir açıklama olmaması talihsiz bir durumdur, çünkü atadığınız her sabit kursu yakalamayı denemekten kaçınmanın harika bir yolunu göstermektedir await!
Jim

0

catchTecrübelerime göre bu şekilde çalışmak tehlikelidir. Yığının tamamında atılan herhangi bir hata, yalnızca bu vaatten kaynaklanan bir hata değil (muhtemelen istediğiniz şey bu değildir) yakalanacaktır.

Bir sözün ikinci argümanı zaten bir reddedilme / başarısızlık geri aramadır. Bunun yerine onu kullanmak daha iyi ve daha güvenli.

İşte bunu halletmek için yazdığım bir typcript typafe tek satırlık:

function wait<R, E>(promise: Promise<R>): [R | null, E | null] {
  return (promise.then((data: R) => [data, null], (err: E) => [null, err]) as any) as [R, E];
}

// Usage
const [currUser, currUserError] = await wait<GetCurrentUser_user, GetCurrentUser_errors>(
  apiClient.getCurrentUser()
);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.