Javascript merak vaad ediyor


96

Bu sözü çağırdığımda, çıktı işlev çağrılarının sırası ile uyuşmuyor. .thenÖnce gelir .catchile söz olsa da, .thensonrasında denilen ediliyordu. Bunun nedeni nedir?

const verifier = (a, b) =>
  new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));

verifier(3, 4)
  .then((response) => console.log("response: ", response))
  .catch((error) => console.log("error: ", error));

verifier(5, 4)
  .then((response) => console.log("response: ", response))
  .catch((error) => console.log("error: ", error));

çıktı

node promises.js
response: true
error: false

34
Bağımsız vaat zincirleri arasındaki zamanlamalara asla güvenmemelisiniz.
Bergi

Yanıtlar:


136

Bu, altına inmek için harika bir soru.

Bunu yaptığınızda:

verifier(3,4).then(...)

bu, yeni reddedilen sözün .catch()takip eden işleyiciyi çalıştırabilmesi için olay döngüsüne başka bir döngü gerektiren yeni bir söz döndürür . Bu ekstra döngü bir sonraki sırayı verir:

verifier(5,4).then(...)

.then()işleyicisini önceki satırdan önce çalıştırma şansı, .catch()çünkü ilk satırdaki .catch()işleyici kuyruğa girmeden ve öğeler kuyruktan FIFO sırasına göre çalıştırılmadan önce zaten kuyruktaydı .


.then(f1, f2)Formu yerine kullanırsanız, .then().catch()beklediğinizde çalışacağını unutmayın, çünkü ek bir söz yoktur ve bu nedenle ek bir onay söz konusu değildir:

const verifier = (a, b) =>
  new Promise((resolve, reject) => (a > b ? resolve(true) : reject(false)));

verifier(3, 4)
  .then((response) => console.log("response (3,4): ", response),
        (error) => console.log("error (3,4): ", error)
  );

verifier(5, 4)
  .then((response) => console.log("response (5,4): ", response))
  .catch((error) => console.log("error (5,4): ", error));

Ayrıca, tüm mesajları etiketledim, böylece hangi verifier()aramadan geldiklerini görebilirsiniz , bu da çıktıyı okumayı çok daha kolay hale getirir.


ES6 Söz geri arama sıralaması ve daha ayrıntılı açıklama hakkında spesifikasyon

(Bir bir geri deyişiyle ES6 Spec bize bu söz "işler" söyler .then()veya .catch()onlar iş kuyruğuna takıldığında dayalı FIFO sırayla çalıştırılır). Özel olarak FIFO adını vermez, ancak kuyruğun sonuna yeni işlerin eklendiğini ve işlerin kuyruğun başından itibaren çalıştırıldığını belirtir. Bu, FIFO siparişini uygular.

PerformPromiseThen (geri aramayı yürütür .then()) EnqueueJob'a yol açar, bu da çözümleme veya reddetme işleyicisinin gerçekte çalıştırılmak üzere planlandığı şekilde gerçekleşir. EnqueueJob, bekleyen işin iş kuyruğunun arkasına eklendiğini belirtir. Ardından NextJob işlemi, öğeyi kuyruğun önünden çeker. Bu, Promise iş kuyruğundan işlerin hizmetinde FIFO siparişini sağlar.

Dolayısıyla, orijinal sorudaki örnekte, verifier(3,4)sözün geri aramalarını ve verifier(5,4)çalıştırıldıkları sırayla iş kuyruğuna eklenen sözün her ikisi de orijinal sözler yerine getirildiğinden alıyoruz . Ardından, yorumlayıcı olay döngüsüne geri döndüğünde, önce verifier(3,4)işi alır. Bu söz reddedildi ve bunun için verifier(3,4).then(...). Yani, yaptığı şey verifier(3,4).then(...)geri dönen sözü reddetmektir ve bu da verifier(3,4).then(...).catch(...)işleyicinin jobQueue'ya eklenmesine neden olur .

Daha sonra olay döngüsüne geri döner ve jobQueue'dan aldığı bir sonraki iş verifier(5, 4)iştir. Çözülmüş bir sözü ve bir çözümleyici işleyiciye sahip olduğundan, bu işleyiciyi çağırır. Bu, response (5,4):çıktının gösterilmesine neden olur .

Daha sonra olay döngüsüne geri döner ve jobQueue'dan çektiği bir sonraki iş, verifier(3,4).then(...).catch(...)bunu çalıştırdığı iştir ve bu da error (3,4)çıktının gösterilmesine neden olur .

Bunun nedeni, .catch()1. zincirin, .then()bildirdiğiniz siparişe neden olan, 2. zincirdekinden daha derin bir vaat seviyesi olmasıdır. Bunun nedeni, vaat zincirlerinin eşzamanlı olarak değil, FIFO sırasındaki iş kuyruğu aracılığıyla bir seviyeden diğerine geçmesidir.


Bu Seviye Çizelgeleme Ayrıntılarına Güvenmek Hakkında Genel Öneri

Bilginize, genel olarak, bu detaylı zamanlama bilgisine bağlı olmayan bir kod yazmaya çalışıyorum. Tuhaf ve bazen anlaşılması yararlı olsa da, kodda basit görünen zararsız bir değişiklik göreceli zamanlamada bir değişikliğe yol açabileceğinden kırılgan bir koddur. Öyleyse, zamanlama bunun gibi iki zincir arasında kritikse, kodu, bu ayrıntılı anlayış düzeyine güvenmektense, zamanlamayı istediğim şekilde zorlayacak şekilde yazmayı tercih ederim.


Daha spesifik olmak gerekirse, bu kesin davranış, vaatlerin şartnamesinde herhangi bir yerde belgelenmemiştir, bu da bunu bir uygulama ayrıntısı yapar. Tercümanlar arasında (örn. Node.js vs Edge vs Firefox) veya yorumlayıcı sürümleri arasında (örn. Düğüm 12 ve Düğüm 14) farklı davranışlar görebilirsiniz. Spesifikasyon yalnızca, vaatlerin zalgo kodundan kaçınmak için eşzamansız olarak işlendiğini söylüyor (ki bu, potansiyel olarak eşzamansız kodun zamanlamasına bağlı olmak isteyen bu tür sorular soran insanlar tarafından motive edildiği için IMHO, BTW'yi yanlış yönlendiriyordu)
slebetman

@slebetman - Ayrı sözlerden gelen sözlü geri aramaların kuyruğa ne zaman eklendiklerine bağlı olarak FIFO olarak adlandırıldığı ve bir sonraki tıklamaya kadar çalışamayacağı belgelenmemiş mi? Görünüşe göre FIFO sıralaması burada gerekli .then()olan tek şey, çünkü bu sıralamaya yol açan gelecekteki bir tik üzerinde eşzamansız olarak çözmesi / reddetmesi gereken yeni bir söz vermek zorundadır. Rakip geri aramaların FIFO sıralamasını kullanmayan herhangi bir uygulama biliyor musunuz?
jfriend00

3
@slebetman Promises / A + bunu belirtmez. ES6 bunu belirtir. (ES11 awaityine de davranışını değiştirdi ).
Bergi

Kuyruk sırasındaki ES6 spesifikasyonundan. çözme veya reddetme işleyicisinin çağrılmak üzere planlandığı PerformPromiseThenşekilde EnqueueJobhangisine yönlendirir . EnqueueJob, bekleyen işin iş kuyruğunun arkasına eklendiğini belirtir. Ardından NextJob işlemi, öğeyi kuyruğun önünden çeker. Bu, Promise iş kuyruğunda FIFO siparişini sağlar.
jfriend00

@Bergi ES11'deki bu değişiklik nedir await? Bir bağlantı yeterlidir. Teşekkürler!!
Pedro A

49

Promise.resolve()
  .then(() => console.log('a1'))
  .then(() => console.log('a2'))
  .then(() => console.log('a3'))
Promise.resolve()
  .then(() => console.log('b1'))
  .then(() => console.log('b2'))
  .then(() => console.log('b3'))

A1, a2, a3, b1, b2, b3 çıktısı yerine aynı nedenden dolayı a1, b1, a2, b2, a3, b3 göreceksiniz - her biri bir söz verir ve olay döngüsünün sonuna gider kuyruk. Yani bu "vaat yarışını" görebiliriz. Aynı şey, bazı iç içe geçmiş vaatler olduğunda da geçerlidir.

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.