JavaScript Sözleri: Örneklerle Bir Eğitim
Yayınlanan: 2022-03-11Vaatler, JavaScript geliştirme çevrelerinde sıcak bir konudur ve kesinlikle onlarla tanışmalısınız. Kafanızı sarmak kolay değil; bunları anlamak için birkaç öğretici, örnek ve yeterli miktarda alıştırma gerekebilir.
Bu eğitimdeki amacım, JavaScript Sözlerini anlamanıza yardımcı olmak ve onları daha fazla kullanma alıştırması yapmanız için sizi teşvik etmektir. Sözlerin ne olduğunu, hangi sorunları çözdüklerini ve nasıl çalıştıklarını açıklayacağım. Bu makalede açıklanan her adıma, birlikte çalışmanıza yardımcı olacak ve daha fazla araştırma için temel olarak kullanılacak bir jsbin
kodu örneği eşlik eder.
JavaScript vaadi nedir?
Söz, sonunda bir değer üreten bir yöntemdir. Bir alıcı fonksiyonunun asenkron karşılığı olarak düşünülebilir. Özü şu şekilde açıklanabilir:
promise.then(function(value) { // Do something with the 'value' });
Sözler, geri aramaların asenkron kullanımının yerini alabilir ve onlara göre çeşitli faydalar sağlar. Gittikçe daha fazla kitaplık ve çerçeve onları eşzamansızlığı ele almanın birincil yolu olarak benimsedikçe zemin kazanmaya başlıyorlar. Ember.js, böyle bir çerçevenin harika bir örneğidir.
Promises/A+ belirtimini uygulayan birkaç kitaplık vardır. Temel kelimeleri öğreneceğiz ve arkasındaki kavramları pratik bir şekilde tanıtmak için birkaç JavaScript vaat örneği üzerinde çalışacağız. Kod örneklerinde daha popüler uygulama kitaplıklarından biri olan rsvp.js'yi kullanacağım.
Hazır olun, bir sürü zar atacağız!
rsvp.js kitaplığını alma
Sözler ve dolayısıyla rsvp.js, hem sunucu hem de istemci tarafında kullanılabilir. nodejs için yüklemek için proje klasörünüze gidin ve şunu yazın:
npm install --save rsvp
Ön uçta çalışıyorsanız ve bower kullanıyorsanız, bu sadece
bower install -S rsvp
uzakta.
Doğrudan oyuna girmek istiyorsanız, basit komut dosyası etiketi ile dahil edebilirsiniz (ve jsbin
ile "Kitaplık ekle" açılır menüsünden ekleyebilirsiniz):
<script src="//cdn.jsdelivr.net/rsvp/3.0.6/rsvp.js"></script>
Bir söz hangi özelliklere sahiptir?
Bir söz üç durumdan birinde olabilir: beklemede , yerine getirildi veya reddedildi . Oluşturulduğunda, söz bekleme durumundadır. Buradan, tamamlanmış veya reddedilmiş duruma geçebilir. Bu geçişe sözün çözülmesi diyoruz. Bir sözün çözülmüş hali onun nihai halidir, bu yüzden bir kez yerine getirildikten veya reddedildikten sonra orada kalır.
rsvp.js'de bir söz oluşturmanın yolu, açıklayıcı bir kurucu olarak adlandırılan şeydir. Bu tür yapıcı, tek bir işlev parametresi alır ve onu hemen iki argümanla çağırır: fulfill
ve reject
; bu, sözü fulfilled
veya rejected
durumuna geçirebilir:
var promise = new RSVP.Promise(function(fulfill, reject) { (...) });
Bu JavaScript söz kalıbına açıklayıcı yapıcı denir, çünkü tek işlev argümanı, yapıcı işlevine yeteneklerini gösterir, ancak söz tüketicilerinin durumunu manipüle edememesini sağlar.
Sözün tüketicileri, durum değişikliklerine işleyicilerini then
yöntemiyle ekleyerek tepki verebilir. Her ikisi de eksik olabilecek bir yerine getirme ve bir reddetme işleyici işlevi alır.
promise.then(onFulfilled, onRejected);
Sözün çözüm sürecinin sonucuna bağlı olarak, onFulfilled
veya onRejected
işleyicisi eşzamansız olarak çağrılır.
İşlerin hangi sırayla yürütüldüğünü gösteren bir örnek görelim:
function dieToss() { return Math.floor(Math.random() * 6) + 1; } console.log('1'); var promise = new RSVP.Promise(function(fulfill, reject) { var n = dieToss(); if (n === 6) { fulfill(n); } else { reject(n); } console.log('2'); }); promise.then(function(toss) { console.log('Yay, threw a ' + toss + '.'); }, function(toss) { console.log('Oh, noes, threw a ' + toss + '.'); }); console.log('3');
Bu snippet, aşağıdakine benzer bir çıktı yazdırır:
1 2 3 Oh, noes, threw a 4.
Ya da şansımız yaver giderse şunu görürüz:
1 2 3 Yay, threw a 6.
Bu, öğreticinin iki şeyi gösterdiğini vaat ediyor.
İlk olarak, söze eklediğimiz işleyiciler, diğer tüm kodlar eşzamansız olarak çalıştırıldıktan sonra gerçekten çağrıldı.
İkincisi, yerine getirme işleyicisinin yalnızca söz yerine getirildiğinde, çözüldüğü değerle çağrılmasıdır (bizim durumumuzda, zar atışının sonucu). Aynısı reddetme işleyicisi için de geçerlidir.
Zincirleme sözler ve aşağı damlama
Spesifikasyon, then
işlevinin (işleyicilerin) de bir söz vermesini gerektirir; bu, zincirleme vaatlerini birlikte etkinleştirerek neredeyse eşzamanlı görünen kodla sonuçlanır:
signupPayingUser .then(displayHoorayMessage) .then(queueWelcomeEmail) .then(queueHandwrittenPostcard) .then(redirectToThankYouPage)
Burada, signupPayingUser
bir söz döndürür ve söz zincirindeki her işlev, tamamlandıktan sonra önceki işleyicinin dönüş değeriyle çağrılır. Tüm pratik amaçlar için, bu, ana yürütme iş parçacığını engellemeden çağrıları seri hale getirir.
Her sözün zincirdeki bir önceki öğenin dönüş değeriyle nasıl çözüldüğünü görmek için zarları atmaya dönüyoruz. Zarı en fazla üç kez veya ilk altısı jsbin gelene kadar atmak istiyoruz:
function dieToss() { return Math.floor(Math.random() * 6) + 1; } function tossASix() { return new RSVP.Promise(function(fulfill, reject) { var n = Math.floor(Math.random() * 6) + 1; if (n === 6) { fulfill(n); } else { reject(n); } }); } function logAndTossAgain(toss) { console.log("Tossed a " + toss + ", need to try again."); return tossASix(); } function logSuccess(toss) { console.log("Yay, managed to toss a " + toss + "."); } function logFailure(toss) { console.log("Tossed a " + toss + ". Too bad, couldn't roll a six"); } tossASix() .then(null, logAndTossAgain) //Roll first time .then(null, logAndTossAgain) //Roll second time .then(logSuccess, logFailure); //Roll third and last time
Bu vaatler örnek kodunu çalıştırdığınızda, konsolda şöyle bir şey göreceksiniz:
Tossed a 2, need to try again. Tossed a 1, need to try again. Tossed a 4. Too bad, couldn't roll a six.
tossASix
tarafından döndürülen söz, toss altı olmadığında reddedilir, bu nedenle ret işleyicisi gerçek toss ile çağrılır. logAndTossAgain
, konsolda sonuçlanan yazdırır ve başka bir zar atışını temsil eden bir söz verir. Bu fırlatma da bir sonraki logAndTossAgain
tarafından reddedilir ve oturumdan çıkar.
Ancak bazen şanslısınız* ve altıyı atmayı başarırsınız:
Tossed a 4, need to try again. Yay, managed to toss a 6.
* O kadar şanslı olmanıza gerek yok. Üç zar atarsanız, en az bir altılık atmak için ~%42 şans vardır.

Bu örnek ayrıca bize bir şey daha öğretiyor. İlk başarılı altı atıştan sonra nasıl daha fazla atış yapılmadığını gördünüz mü? Zincirdeki tüm yerine getirme işleyicilerinin ( then
çağrılarındaki ilk bağımsız değişkenler), sonuncusu logSuccess
dışında null
olduğuna dikkat edin. Belirtim, bir işleyici (yerine getirme veya reddetme) bir işlev değilse, döndürülen sözün aynı değerle çözülmesi (yerine getirilmesi veya reddedilmesi) gerektiğini gerektirir. Yukarıdaki söz örneğinde, yerine getirme işleyicisi null
, bir işlev değildir ve sözün değeri 6 ile yerine getirilmiştir. Öyleyse, then
çağrısı (zincirdeki bir sonraki) tarafından döndürülen söz de yerine getirilecek. değeri olarak 6 ile.
Bu, gerçek bir yerine getirme işleyicisi (bir işlev olan) mevcut olana kadar tekrarlanır, bu nedenle yerine getirme, işlenene kadar damlar. Bizim durumumuzda bu, konsolda neşeyle oturumun kapatıldığı zincirin sonunda olur.
Hataları işleme
Sözler/A+ belirtimi, bir söz reddedilirse veya bir reddetme işleyicisinde bir hata atılırsa, bunun kaynaktan "aşağı akış" olan bir reddetme işleyicisi tarafından ele alınmasını talep eder.
Aşağıdaki damlama tekniğinden yararlanmak, hataları ele almak için temiz bir yol sağlar:
signupPayingUser .then(displayHoorayMessage) .then(queueWelcomeEmail) .then(queueHandwrittenPostcard) .then(redirectToThankYouPage) .then(null, displayAndSendErrorReport)
Reddetme işleyicisi yalnızca zincirin en sonuna eklendiğinden, zincirdeki herhangi bir yerine getirme işleyicisi reddedilirse veya bir hata atarsa, displayAndSendErrorReport
ile karşılaşana kadar damlar.
Sevgili zarlarımıza geri dönelim ve bunu iş başında görelim. Diyelim ki eşzamansız olarak zar atmak ve sonuçları yazdırmak istiyoruz:
var tossTable = { 1: 'one', 2: 'two', 3: 'three', 4: 'four', 5: 'five', 6: 'six' }; function toss() { return new RSVP.Promise(function(fulfill, reject) { var n = Math.floor(Math.random() * 6) + 1; fulfill(n); }); } function logAndTossAgain(toss) { var tossWord = tossTable[toss]; console.log("Tossed a " + tossWord.toUppercase() + "."); } toss() .then(logAndTossAgain) .then(logAndTossAgain) .then(logAndTossAgain);
Bunu çalıştırdığınızda hiçbir şey olmuyor. Görünüşe göre konsolda hiçbir şey yazdırılmaz ve hiçbir hata atılmaz.
Gerçekte, bir hata atılır, zincirde reddetme işleyicisi olmadığı için onu görmeyiz. İşleyicilerdeki kod, yeni bir yığınla eşzamansız olarak yürütüldüğünden, konsoldan çıkış bile yapılmaz. Bunu düzeltelim:
function logAndTossAgain(toss) { var tossWord = tossTable[toss]; console.log("Tossed a " + tossWord.toUpperCase() + "."); } function logErrorMessage(error) { console.log("Oops: " + error.message); } toss() .then(logAndTossAgain) .then(logAndTossAgain) .then(logAndTossAgain) .then(null, logErrorMessage);
Yukarıdaki kodu çalıştırmak hatayı şimdi gösteriyor:
"Tossed a TWO." "Oops: Cannot read property 'toUpperCase' of undefined"
logAndTossAgain
bir şey döndürmeyi unuttuk ve ikinci söz, undefined
ile yerine getirildi. Bir sonraki yerine getirme işleyicisi daha sonra bu toUpperCase
aramaya çalışırken patlar. Bu, hatırlanması gereken bir diğer önemli noktadır: her zaman işleyicilerden bir şey iade edin veya sonraki işleyicilerde hiçbir şeyin geçmemesine hazır olun.
daha yüksek bina
Şimdi bu öğreticinin örnek kodunda JavaScript vaatlerinin temellerini gördük. Bunları kullanmanın büyük bir yararı, istediğimiz davranışla "karmaşık" vaatler üretmek için basit yollarla oluşturulabilmeleridir. rsvp.js
kitaplığı bunlardan bir avuç sağlar ve ilkelleri ve bu üst düzey olanları kullanarak her zaman kendinizinkini oluşturabilirsiniz.
Son, en karmaşık örnek için, karakter puanlarını almak için AD&D rol oynama ve zar atma dünyasına gidiyoruz. Bu puanlar, karakterin her becerisi için üç zar atılarak elde edilir.
Önce kodu buraya yapıştırayım ve sonra neyin yeni olduğunu açıklayayım:
function toss() { var n = Math.floor(Math.random() * 6) + 1; return new RSVP.resolve(n); // [1] } function threeDice() { var tosses = []; function add(x, y) { return x + y; } for (var i=0; i<3; i++) { tosses.push(toss()); } return RSVP.all(tosses).then(function(results) { // [2] return results.reduce(add); // [3] }); } function logResults(result) { console.log("Rolled " + result + " with three dice."); } function logErrorMessage(error) { console.log("Oops: " + error.message); } threeDice() .then(logResults) .then(null, logErrorMessage);
Son kod örneğinden toss
. Sadece bir zar atmanın sonucuyla her zaman yerine getirilen bir söz yaratır. Daha az törenle böyle bir vaat oluşturan uygun bir yöntem olan RSVP.resolve
kullandım (yukarıdaki kodda [1]'e bakın).
threeDice
, her birinin bir zar atışını temsil ettiği 3 vaat oluşturdum ve sonunda bunları RSVP.all
ile birleştirdim. RSVP.all
bir dizi vaat alır ve sıralarını korurken her kurucu söz için bir tane olmak üzere çözümlenmiş değerlerinin bir dizisiyle çözülür. Bu, results
atışların sonucuna sahip olduğumuz anlamına gelir (yukarıdaki kodda [2]'ye bakın) ve bunların toplamı ile yerine getirilen bir söz veririz (yukarıdaki kodda [3]'e bakın).
Ortaya çıkan vaadi çözümlemek, ardından toplam sayıyı günlüğe kaydeder:
"Rolled 11 with three dice"
Gerçek sorunları çözmek için vaatleri kullanmak
JavaScript vaatleri, sebepsiz yere zaman uyumsuz zar atışlarından çok daha karmaşık olan uygulamalardaki sorunları çözmek için kullanılır.
Üç zar atmayı, ayrı uç noktalara üç ajax isteği göndermek ve hepsi başarılı bir şekilde geri döndüğünde (veya herhangi biri başarısız olursa) devam etmekle değiştirirseniz, zaten yararlı bir söz ve RSVP.all
uygulamasına sahipsiniz.
Sözler, doğru kullanıldığında, akıl yürütmesi daha kolay olan ve dolayısıyla geri aramalardan daha kolay hata ayıklaması olan okunması kolay kodlar üretir. Halihazırda spesifikasyonun bir parçası olduklarından, örneğin hata işleme ile ilgili konvansiyonlar kurmaya gerek yoktur.
Bu JavaScript eğitiminde vaatlerin neler yapabileceğinin yüzeyini zar zor çizdik. Promise kitaplıkları, emrinizde olan bir düzine yöntem ve düşük seviyeli kurucu sağlar. Bunlarda ustalaşın ve onlarla yapabileceklerinizin sınırı gökyüzüdür.
Yazar hakkında
Balint Erdi, uzun zaman önce harika bir rol yapma ve AD&D hayranıydı ve şimdi büyük bir söz ve Ember.js hayranı. Değişmeyen şey onun rock & roll tutkusu. Bu nedenle kitaptaki uygulamanın teması olarak rock & roll'u kullanan Ember.js üzerine bir kitap yazmaya karar verdi. Ne zaman başladığını öğrenmek için buradan kaydolun.