Zaman uyumsuz / bekliyor sözdizimi nasıl reddedilir?


282

Eşzamansız / bekleme işlevi tarafından döndürülen bir sözü nasıl reddedebilirim?

Örneğin

foo(id: string): Promise<A> {
  return new Promise((resolve, reject) => {
    someAsyncPromise().then((value)=>resolve(200)).catch((err)=>reject(400))
  });
}

Zaman uyumsuz / beklemesine çevir

async foo(id: string): Promise<A> {
  try{
    await someAsyncPromise();
    return 200;
  } catch(error) {//here goes if someAsyncPromise() rejected}
    return 400; //this will result in a resolved promise.
  });
}

Peki, bu durumda bu vaadi nasıl düzgün bir şekilde reddedebilirim?


20
PromiseYapıcı antipatterninden kaçının ! İlk pasaj bile yazılmalıydıfoo(id: string): Promise<A> { return someAsyncPromise().then(()=>{ return 200; }, ()=>{ throw 400; }); }
Bergi

10
Sorunun TypeScript ile ilgisi olmadığından, bu sorudaki kodu vanilya JS'ye çevirmenin yararlı olacağını düşünüyorum. Bunu yaparsam, bu düzenleme kabul edilebilir mi?
Jacob Ford

Yanıtlar:


328

En iyi seçeneğiniz değeri throwbir Errordeğere sarmaktır, bu da değeri sarmakla reddedilen bir vaatle sonuçlanır Error:

} catch (error) {
    throw new Error(400);
}

Ayrıca sadece throwdeğeri de yapabilirsiniz , ancak yığın izleme bilgisi yoktur:

} catch (error) {
    throw 400;
}

Alternatif olarak, Errordeğeri sararak reddedilen bir söz verin , ancak deyimsel değil:

} catch (error) {
    return Promise.reject(new Error(400));
}

(Ya da sadece return Promise.reject(400);, ama yine, o zaman bağlam bilgisi yoktur.)

(Sizin durumunuzda, kullandığınız TypeScriptve fooretn değeri olduğu için şunu Promise<A>kullanırsınız return Promise.reject<A>(400 /*or error*/);)

Bir async/ awaitdurumda, bu sonuncusu muhtemelen bir anlamsal yanlış eşleşmedir, ancak işe yarar.

Bir atarsanız Error, sözdizimiyle foosonucunuzu tüketen herhangi bir şeyle iyi oynar await:

try {
    await foo();
} catch (error) {
    // Here, `error` would be an `Error` (with stack trace, etc.).
    // Whereas if you used `throw 400`, it would just be `400`.
}

12
Eşzamansız / beklemede, sözdizimini senkronize etmek için eşzamansız akışı geri almakla ilgili throwolduğundan, Promise.reject()IMO'dan daha iyidir . Bunun throw 400farklı bir soru olup olmadığı . OP'de 400'ü reddediyor ve Errorbunun yerine bir reddetmesi gerektiğini iddia edebiliriz .
unional

2
Evet, ancak kod zinciriniz gerçekten async / bekliyorsa, o zaman ..... burada yazmak zor, bana bir cevap olarak demo
unional

1
catch bloğunda size verilen hatanın aksine yeni bir hata atmak istemenizin bir nedeni var mı?
Adrian M

1
@sebastian - Orada ne demek istediğini bilmiyorum. Gelen asyncfonksiyonlar, hiçbir yoktur resolveveya rejectfonksiyonunu. Fonksiyonun vaadini çözmenin ve reddetmenin deyimsel yolları vardır returnve throwbunlar async.
TJ Crowder

1
@ Jan-PhilipGehrcke - Yapabilirsin , ama asla yapmam. Bir örnek oluşturuyor new, bunu açık yapıyor. Ayrıca, bir Erroralt sınıfınız ( class MyError extends Error) varsa dışarıda bırakamayacağınızı unutmayın , bu yüzden ...
TJ Crowder

146

Ayrıca catch(), asenkron işleminizin çağrısından sonra bir işlevi kolayca zincirleyebileceğiniz de belirtilmelidir, çünkü kaputun altında hala bir söz iade edilir.

await foo().catch(error => console.log(error));

Bu şekilde, try/catchsöz dizimini beğenmezseniz önleyebilirsiniz .


1
Eğer asyncişlevimi reddetmek istersem , istisna atarım ve sonra .catch()geri döndüğümde Promise.rejectveya aradığımda olduğu gibi güzelce yakalarım reject. Bunu sevdim!
icl7126

7
Bunun neden kabul edilen cevap olması gerektiğini anlamıyorum. Kabul edilen cevap daha temiz olmakla kalmaz, aynı zamanda tüm awaitrutin hataların üstesinden gelir. Her biri için çok özel durumlar gerekmedikçe, awaitbunları neden böyle yakalamak istediğinizi anlamıyorum. Sadece ben alçakgönüllü görüş.
edgaralienfoe

1
@jablesauce benim kullanım durumum için, sadece her awaitbaşarısızlık ayrı ayrı yakalamak zorunda değil , aynı zamanda hata vaatleri reddetti Promise tabanlı bir çerçeve ile çalışmak gerekiyordu.
Karasik Reuven

Benim için işe yaramadı. URL başarısız olursa catch bloğuna girmiyor gibi görünüyor. [cevap] = bekliyoruz oauthGet ( ${host}/user/permissions/repositories_wrong_url/, accessToken, accessTokenSecret) .catch (err => {logger.error) yanıl ( 'depo izinleri getirilemedi'; geri arama (ERR);})
sn.anurag

1
awaitburada anahtar kelimeye gerek yok.
Ashish Rawat

12

Bir söz alır ve hata yoksa veri içeren bir dizi ve bir hata varsa hatayı döndüren bir sarmalayıcı işlevi oluşturabilirsiniz .

function safePromise(promise) {
  return promise.then(data => [ data ]).catch(error => [ null, error ]);
}

Böyle kullanın ES7 ve bir in zaman uyumsuz fonksiyonu:

async function checkItem() {
  const [ item, error ] = await safePromise(getItem(id));
  if (error) { return null; } // handle error and return
  return item; // no error so safe to use item
}

1
Güzel Go sözdizimine sahip olmak için bir girişim gibi görünüyor, ancak zarafetin çoğu olmadan. Ben sadece çözümün değerini emmek için şaşırtmak için kullanarak kodu bulmak.
Kim

8

Zaman uyumsuzluk işlevini yazmanın daha iyi bir yolu, beklemede olan bir Sözü en başından döndürmek ve daha sonra, sadece reddedilen bir söz vermeyi reddetmek yerine, sözün geri çağrılması sırasında hem reddetme hem de çözünürlükleri işlemek olacaktır. Misal:

async foo(id: string): Promise<A> {
    return new Promise(function(resolve, reject) {
        // execute some code here
        if (success) { // let's say this is a boolean value from line above
            return resolve(success);
        } else {
            return reject(error); // this can be anything, preferably an Error object to catch the stacktrace from this function
        }
    });
}

Sonra sadece geri verilen söz üzerinde yöntemleri zincirlersiniz:

async function bar () {
    try {
        var result = await foo("someID")
        // use the result here
    } catch (error) {
        // handle error here
    }
}

bar()

Kaynak - bu eğitici:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise


5
Soru özellikle async / await kullanımı hakkında sordu.
Mak

Bu cevabın kesin doğru cevap olması amaçlanmamıştı. Bu, yukarıda verilen diğer cevaplara verilen bir destek cevabıdır. Ben yorum olarak koymak olurdu ama kod var göz önüne alındığında, cevap alanı daha iyi bir yerdir.
OzzyTheGiant

Açıkladığınız için teşekkürler. Eşzamansız bir işlevin nasıl yapılacağını göstermek kesinlikle yararlıdır. İkinci kod bloğunun beklemek için güncellenmesi çok daha alakalı ve yararlı olacaktır. Şerefe
Mak

Güncellenmesi için yanıtınızı düzenledim. Bir şey kaçırırsam haberim olsun
Mak

4

Birden fazla try-catch bloğum olmadan reddetmeleri yeni bir yaklaşımla düzgün bir şekilde ele almak için bir önerim var .

import to from './to';

async foo(id: string): Promise<A> {
    let err, result;
    [err, result] = await to(someAsyncPromise()); // notice the to() here
    if (err) {
        return 400;
    }
    return 200;
}

To.ts işlevinin nereden alınacağı :

export default function to(promise: Promise<any>): Promise<any> {
    return promise.then(data => {
        return [null, data];
    }).catch(err => [err]);
}

Krediler aşağıdaki linkte Dima Grossman'a gidiyor .


1
Bu yapıyı neredeyse sadece (çok daha temiz) kullanıyorum ve bir süredir npmjs.com/package/await-to-js için bir 'to' modülü var . Ayrı deklarasyona gerek yok, sadece yapısökümlü atamanın önüne koy. Sadece let [err]=hataları kontrol ediyorsanız da yapabilirsiniz .
DKebler

3

Bu @TJ Crowder'ın cevabı değil. Sadece "" istisna bir reddeye dönüştürülecekse, bu bir Hata ise gerçekten rahatsız olup olmadığımdan emin değilim. Sadece hata atma nedenlerim muhtemelen geçerli değil. "

kodunuz async/ kullanıyorsa await, Errorbunun yerine aşağıdakileri reddetmek iyi bir uygulamadır 400:

try {
  await foo('a');
}
catch (e) {
  // you would still want `e` to be an `Error` instead of `400`
}

3

Bu eski bir soru olduğunu biliyorum, ama ben sadece iş parçacığı üzerinde tökezledi ve burada hata ve ret arasında istisna işleme kullanmak için sık sık tekrarlanan tavsiye afoul (çoğu durumda, en azından) çalışan bir ilişki var gibi görünüyor beklenen vakalarla ilgilenmek. Bunu göstermek için: zaman uyumsuz bir yöntem bir kullanıcının kimliğini doğrulamaya çalışıyorsa ve kimlik doğrulaması başarısız olursa, bu bir hata değil (örneğin, kimlik doğrulama API'si kullanılamıyorsa) reddedilir (beklenen iki durumdan biri).

Sadece kılları bölmediğimden emin olmak için, bu kodu kullanarak üç farklı yaklaşımın performans testini yaptım:

const iterations = 100000;

function getSwitch() {
  return Math.round(Math.random()) === 1;
}

function doSomething(value) {
  return 'something done to ' + value.toString();
}

let processWithThrow = function () {
  if (getSwitch()) {
    throw new Error('foo');
  }
};

let processWithReturn = function () {
  if (getSwitch()) {
    return new Error('bar');
  } else {
    return {}
  }
};

let processWithCustomObject = function () {
  if (getSwitch()) {
    return {type: 'rejection', message: 'quux'};
  } else {
    return {type: 'usable response', value: 'fnord'};
  }
};

function testTryCatch(limit) {
  for (let i = 0; i < limit; i++) {
    try {
      processWithThrow();
    } catch (e) {
      const dummyValue = doSomething(e);
    }
  }
}

function testReturnError(limit) {
  for (let i = 0; i < limit; i++) {
    const returnValue = processWithReturn();
    if (returnValue instanceof Error) {
      const dummyValue = doSomething(returnValue);
    }
  }
}

function testCustomObject(limit) {
  for (let i = 0; i < limit; i++) {
    const returnValue = processWithCustomObject();
    if (returnValue.type === 'rejection') {
      const dummyValue = doSomething(returnValue);
    }
  }
}

let start, end;
start = new Date();
testTryCatch(iterations);
end = new Date();
const interval_1 = end - start;
start = new Date();
testReturnError(iterations);
end = new Date();
const interval_2 = end - start;
start = new Date();
testCustomObject(iterations);
end = new Date();
const interval_3 = end - start;

console.log(`with try/catch: ${interval_1}ms; with returned Error: ${interval_2}ms; with custom object: ${interval_3}ms`);

Javascript tercümanı hakkındaki belirsizliğim nedeniyle orada bulunan bazı şeyler dahil edilmiştir (Bir seferde sadece bir tavşan deliğinden aşağı inmeyi seviyorum); örneğin, doSomethingişlevi dahil ettim dummyValueve koşullu blokların optimize edilmemesini sağlamak için dönüşünü atadım.

Sonuçlarım:

with try/catch: 507ms; with returned Error: 260ms; with custom object: 5ms

Küçük optimizasyonları avlamanın zahmetine değmeyeceği birçok durum olduğunu biliyorum, ancak daha büyük ölçekli sistemlerde bu şeyler büyük bir kümülatif fark yaratabilir ve bu oldukça keskin bir karşılaştırmadır.

SO… kabul edilen yanıtın yaklaşımının, bir zaman uyumsuzluk işlevi içinde öngörülemeyen hataları ele almayı beklediğiniz durumlarda sağlam olduğunu düşünürken, reddetme basitçe "Plan B'ye (veya C veya D…) "Bence tercihim özel bir yanıt nesnesi kullanarak reddetmek olacaktır.


2
Ayrıca, söz konusu işleve yapılan çağrı kapalı alanda bir try / catch bloğu içindeyse, vaatlerden farklı olarak, zaman uyumsuzluk hatalarını kapsama alanı, burada o kapsamın yerel hataları gibi ele alınır. Bu, asenkron / beklemenin ana avantajlarından biridir!
RiqueW

Mikrobenç işaretleri şeytandır. Sayılara daha yakından bakın. Burada 1ms'lik bir fark fark etmek için 1000x bir şey yapmalısınız. Evet, atma / yakalama işlevi işlevi deoptimize eder. Ancak a) asenkron bir şey bekliyorsanız, arka planda gerçekleşmesi için 0.0005 Ms'den daha uzun süren birkaç büyüklük sipariş alması muhtemeldir. b) Burada 1 ms fark yaratmak için 1000x yapmalısınız.
Jamie Pate
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.