Açık söz inşaat antipattern nedir ve nasıl önleyebilirim?


516

Ben benziyor bir şey yapar kod yazıyordu:

function getStuffDone(param) {           | function getStuffDone(param) {
    var d = Q.defer(); /* or $q.defer */ |     return new Promise(function(resolve, reject) {
    // or = new $.Deferred() etc.        |     // using a promise constructor
    myPromiseFn(param+1)                 |         myPromiseFn(param+1)
    .then(function(val) { /* or .done */ |         .then(function(val) {
        d.resolve(val);                  |             resolve(val);
    }).catch(function(err) { /* .fail */ |         }).catch(function(err) {
        d.reject(err);                   |             reject(err);
    });                                  |         });
    return d.promise; /* or promise() */ |     });
}                                        | }

Birisi bana buna sırasıyla " ertelenmiş antipattern " veya " Promiseyapıcı antipattern " denir, bu kodda kötü olan nedir ve buna neden antipattern denir ?


Bunun kaldırılmasının (sağ bağlamda, solda değil, örnek olarak) getStuffDoneişlev sarmalayıcısını kaldırmak ve yalnızca Promise değişmezini kullanmak olduğunu doğrulayabilir miyim ?
Dembinski

1
veya catchbloğun getStuffDoneambalajında ​​antipattern olması mı?
Dembinski

1
En azından yerel Promiseörnek için .thenve .catchişleyicileri için gereksiz işlev sarmalayıcıları da var (yani, sadece olabilir .then(resolve).catch(reject).) Mükemmel bir anti-desen fırtınası.
Noah Freitas

6
@NoahFreitas bu kod didaktik amaçlar için bu şekilde yazılmıştır. Ben böyle bir kod okuduktan sonra bu sorunla karşılaşan insanlara yardımcı olmak için bu soru ve cevabı yazdım :)
Benjamin Gruenbaum

Sadece açık Promise yapısını değil, aynı zamanda küresel bir değişkenin kullanımını ortadan kaldırmak için stackoverflow.com/questions/57661537/… 'a bakın .
David Spector

Yanıtlar:


357

Ertelenmiş antipattern (şimdi açık-inşaat antipattern) tarafından ortaya Esailija Ben ilk sözleri kullanıldığında sözler yapmak için yeni bir ortak antipattern kişidir, kendim yaptım. Yukarıdaki kod ile ilgili sorun zincir vaat gerçeğini kullanmak başarısız olmasıdır.

Sözler zincirlenebilir .thenve sözleri doğrudan iade edebilirsiniz. İçindeki kodunuz getStuffDoneşu şekilde yeniden yazılabilir:

function getStuffDone(param){
    return myPromiseFn(param+1); // much nicer, right?
}

Vaatler, asenkron kodu daha okunaklı hale getirmekle ilgilidir ve bu gerçeği gizlemeden senkron kod gibi davranır. Vaatler, bir kerelik bir işlemin değeri üzerinde bir soyutlamayı temsil eder, bir programlama dilinde bir ifade veya ifade kavramını soyutlarlar.

Ertelenmiş nesneleri yalnızca bir API'yi vaatlere dönüştürürken ve otomatik olarak yapamazken veya bu şekilde daha kolay ifade edilen toplama işlevleri yazarken kullanmalısınız.

Alıntı Esailija:

Bu en yaygın anti-modeldir. Vaatleri gerçekten anlamadığınızda ve bunları yüceltilmiş olay yayıcıları veya geri arama aracı olarak düşünmediğinizde buna düşmek kolaydır. Özetleyelim: vaatler, düz girinti ve bir istisna kanalı gibi senkron kodun kayıp özelliklerinin çoğunu korumak için eşzamansız kod oluşturmakla ilgilidir.


@BenjaminGruenbaum: Bunun için ertelemeler kullanmamdan eminim, bu yüzden yeni bir soruya gerek yok. Ben sadece cevabınızdan kaçırdığınız bir kullanım örneği olduğunu düşündüm. Yaptığım şey daha çok kümelenmenin tersi gibi görünüyor, değil mi?
mhelvens

1
@mhelvens Geri arama dışı bir API'yı, "geri arama API'sını vaatlere dönüştürme" bölümüne uyan bir promise API'sine el ile bölüyorsanız. Antipattern, bir sözün başka bir vaatte iyi bir sebep olmaksızın sarılmasıyla ilgilidir, başlamak için bir söz sarmazsınız, bu yüzden burada geçerli değildir.
Benjamin Gruenbaum

@BenjaminGruenbaum: Ah, ertelenmiş olsalar da, kendilerini anti-desen olarak kabul ettim, mavi kuşla onları neyin tasvip etmediği ve "bir API'yi vaatlere dönüştürmekten" bahsediyorsunuz (bu da başlamak için bir vaat sarmama durumudur).
mhelvens

mhelvens Sanırım aşırı ertelenmiş anti-desen aslında yaptığı için daha doğru olurdu. Bluebird api'yi .defer()daha yeni (ve güvenli atma) vaat kurucusuna itiraz etti, vaat oluşturma fikrini (hiçbir şekilde) reddetmedi :)
Benjamin Gruenbaum

1
Teşekkürler @ Roamer-1888 referansınız nihayet sorunumun ne olduğunu bulmama yardımcı oldu. Görünüşe göre iç içe (iade edilmemiş) vaatler yaratmıştım.
ghuroo

134

Bunun nesi var?

Ama desen işe yarıyor!

Seni şanslı. Ne yazık ki, büyük olasılıkla bazı büyük durumu unuttuğunuz gibi, muhtemelen değil. Gördüğüm olayların yarısından fazlasında, yazar hata işleyicisiyle ilgilenmeyi unuttu:

return new Promise(function(resolve) {
    getOtherPromise().then(function(result) {
        resolve(result.property.example);
    });
})

Diğer vaat reddedilirse, bu yeni vaadin (ele alınacağı yerde) yayılmak yerine fark edilmeyecektir - ve yeni vaat sonsuza kadar beklemektedir, bu da sızıntılara neden olabilir.

Aynı şey, geri arama kodunuzun hataya neden olması durumunda da olur - örneğin result, a propertyve istisna atılmadığında. Bu işlenmeyecek ve yeni vaadi çözülmeyecekti.

Buna karşılık, kullanmak .then()her iki senaryoyu da otomatik olarak halleder ve bir hata oluştuğunda yeni vaadi reddeder:

 return getOtherPromise().then(function(result) {
     return result.property.example;
 })

Ertelenmiş antipattern sadece hantal değil, aynı zamanda hataya yatkındır . .then()Zincirleme için kullanmak çok daha güvenlidir.

Ama her şeyi hallettim!

Gerçekten mi? İyi. Ancak, özellikle iptal veya mesaj iletme gibi diğer özellikleri destekleyen bir söz kitaplığı kullanıyorsanız, oldukça ayrıntılı ve bol olacaktır. Ya da belki gelecekte, ya da kütüphanenizi daha iyi biriyle değiştirmek mi istiyorsunuz? Bunun için kodunuzu yeniden yazmak istemezsiniz.

Kütüphanelerin yöntemleri ( then) yalnızca tüm özellikleri yerel olarak desteklemez, aynı zamanda belirli optimizasyonlara da sahip olabilirler. Bunları kullanmak muhtemelen kodunuzu daha hızlı hale getirecek veya en azından kütüphanenin gelecekteki revizyonları ile optimize edilmesine izin verecektir.

Nasıl önleyebilirim?

Bu nedenle, manuel olarak bir Promiseveya veya Deferredvarolan vaatler söz konusu olduğunda her bulduğunuzda , önce kütüphane API'sını kontrol edin . Ertelenmiş antipattern genellikle vaatleri bir gözlemci örüntüsü olarak gören insanlar tarafından uygulanır - ancak vaatler geri çağrılardan daha fazlasıdır : kompostlanabilir olmaları gerekir. Her iyi kütüphane, uğraşmak istemediğiniz tüm düşük seviyeli şeylere dikkat ederek, vaatlerin her türlü düşünülebilir şekilde kompozisyonu için kullanımı kolay birçok fonksiyona sahiptir.

Vaat edilen bir yardımcı işlev tarafından desteklenmeyen yeni bir şekilde bazı vaatler oluşturma ihtiyacı bulduysanız, kendi işlevinizi kaçınılmaz Ertelemeler ile yazmak son seçeneğiniz olmalıdır. Daha özellikli bir kitaplığa geçmeyi ve / veya mevcut kitaplığınıza karşı bir hata vermeyi düşünün. Bakıcısı, kompozisyonu mevcut işlevlerden türetebilmeli, sizin için yeni bir yardımcı işlev uygulayabilmeli ve / veya ele alınması gereken uç durumları tanımlamaya yardımcı olmalıdır.


setTimeoutKurucu kullanılabilecek, ancak "Promise kurucu anitpattern" olarak düşünülemeyecek bir işlev dışında örnekler var mı?
guest271314

1
@ guest271314: Bir söz vermeyen eşzamansız her şey. Her ne kadar yeterli olsa da, kütüphanelerin özel taahhüt yardımcıları ile daha iyi sonuçlar elde edersiniz. Ve her zaman en düşük düzeyde vaat ettiğinizden emin olun, bu yüzden " dahil bir işlevsetTimeout " değil, " işlevin setTimeoutkendisi " dir.
Bergi

"Ve her zaman en düşük düzeyde vaat ettiğinizden emin olun, bu yüzden" içeren bir işlev setTimeout"değil," işlevin setTimeoutkendisi "" İkisi arasındaki farklarla bağlantı kurabilir mi?
guest271314

@ guest271314 Yalnızca çağrı içeren bir işlev, işlevin kendisindensetTimeout açıkça farklıdır , değil mi? setTimeout
Bergi

4
Bence buradaki önemli derslerden biri, şimdiye kadar açıkça ifade edilmemiş olan bir vaat ve onun zincirleme 'o zaman' bir eşzamansız işlemi temsil ediyor: ilk işlem Promise yapıcısında ve nihai uç nokta ' sonra 'işlevi. Dolayısıyla, bir senkronizasyon işlemi ve ardından bir asenkron işlemi varsa, senkronizasyon öğelerini Promise'a koyun. Eşzamanlama ve ardından bir senkronizasyon işleminiz varsa, senkronizasyon öğelerini 'o zaman' içine koyun. İlk durumda, orijinal Sözü iade edin. İkinci durumda, Promise / sonra zincirini (aynı zamanda bir Promise) iade edin.
David Spector
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.