+ = İle zaman uyumsuz işlev


63

let x = 0;

async function test() {
    x += await 5;
    console.log('x :', x);
}

test();
x += 1;
console.log('x :', x);

Değerleri xkaydedilir 1ve 5. Benim sorum: x 5ikinci kütükteki değer neden ?

Eğer testsonra çalıştırılır x += 1(bir zaman uyumsuz fonksiyonu olduğu için), daha sonra, x değeri zaman 1 olduğu testyürütülür, yani x += await 5bir değere sahip olması gerekir x 6.


1
await (x += 5) Ve arasındaki farkı bilmelisiniz x += await 5.
Singhi John

Yanıtlar:


60

TL; DR: Çünkü daha önce +=okur x, ancak değiştirildikten sonra await, ikinci işlenenindeki (sağ taraftaki) anahtar kelime nedeniyle yazar .


asyncilk awaitifadeye kadar çağrıldığında işlevler eşzamanlı olarak çalışır .

Yani, kaldırırsanız await, normal bir işlev gibi davranır (ancak bir Promise döndürmesi dışında).

Bu durumda, elde 5ve 6konsolda:

let x = 0;

async function test() {
  x += 5;
  console.log('x :', x);
}

test();
x += 1;
console.log('x :', x);

Birincisi await, argümanı senkronize olarak mevcut olsa bile senkronize çalışmayı durdurur, böylece aşağıdakiler geri döner 1ve 6beklediğiniz gibi:

let x = 0;

async function test() {
  // Enter asynchrony
  await 0;

  x += 5;
  console.log('x :', x);
}

test();
x += 1;
console.log('x :', x);

Ancak, davanız biraz daha karmaşık.

Bunu awaitkullanan bir ifadenin içine koydunuz +=.

Muhtemelen bilirsiniz, JS'de bununla x += yaynıdır x = (x + y). İkinci formu daha iyi anlamak için kullanacağım:

let x = 0;

async function test() {
  x = (x + await 5);
  console.log('x :', x);
}

test();
x += 1;
console.log('x :', x);

Tercüman bu çizgiye ulaştığında ...

x = (x + await 5);

... değerlendirmeye başlar ve ...

x = (0 + await 5);

... sonra ulaşır awaitve durur.

İşlev çağrısından sonraki kod çalışmaya başlar ve değerini değiştirir x, sonra günlüğe kaydeder.

xşimdi 1.

Ardından, ana komut dosyası çıktıktan sonra, yorumlayıcı duraklatılmış testişleve geri döner ve bu satırı değerlendirmeye devam eder:

x = (0 + 5);

Ve, değeri xzaten ikame edildiğinden, kalır 0.

Son olarak, tercüman toplama, mağaza yapar 5için xve bunu kaydeder.

Bir nesne özelliği alıcı / ayarlayıcısının içine giriş yaparak bu davranışı kontrol edebilirsiniz (bu örnekte, y.zdeğerini yansıtır x:

let x = 0;
const y = {
  get z() {
    console.log('get x :', x);
    return x;
  },
  set z(value) {
    console.log('set x =', value);
    x = value;
  }
};

async function test() {
  console.log('inside async function');
  y.z += await 5;
  console.log('x :', x);
}

test();
console.log('main script');
y.z += 1;
console.log('x :', x);

/* Output:

inside async function
get x : 0   <-- async fn reads
main script
get x : 0
set x = 1
x : 1
set x = 5   <-- async fn writes
x : 5       <-- async fn logs

*/
/* Just to make console fill the available space */
.as-console-wrapper {
  max-height: 100% !important;
}


Belki de dikkat çekmeye değer: "Muhtemelen biliyorsunuz, bu x += yaynı x = (x + y)." - Bu, her dilde her durumda geçerli değildir, ancak genel olarak aynı şekilde davrananlara güvenebilirsiniz.
Nick

11

İfadende x += await 5için desugars

const _temp = x;
await;
x = _temp + 5;

_tempOrary değerdir 0ve değiştirirseniz xsırasında await(kodunuzu yapar) o atanır, önemli değil 5sonradan.


9

Bu kodu takip etmek oldukça karmaşıktır, çünkü beklenmedik bir asenkron atlamaları ileri geri atlar. Şimdi nasıl yürütüleceğini (yakından) inceleyelim ve nedenini açıklayacağım. Ayrıca, bir sayı eklemek için konsol günlüklerini değiştirdim - bunlara atıfta bulunmayı kolaylaştırır ve nelerin günlüğe kaydedildiğini daha iyi gösterir:

let x = 0;                        // 1 declaring and assigning x

async function test() {           // 2 function declaration
    x += await 5;                 // 4/7 assigning x
    console.log('x1 :', x);       // 8 printing
}

test();                           // 3 invoking the function
x += 1;                           // 5 assigning x
console.log('x2 :', x);           // 6 printing

Yani, kod aslında düz bir şekilde gitmiyor, bu kesin. Bizim de garip bir şeyimiz var 4/7. Ve bu gerçekten problemin bütünüdür.

Her şeyden önce, açıklığa kavuşturalım - asenkron işlevler aslında kesinlikle asenkron değildir . Yalnızca yürütmeyi duraklatır ve daha sonra awaitanahtar kelime kullanılırsa devam ederler . Bu olmadan, ifadeden sonra ifadeyi eşzamanlı olarak yukarıdan aşağıya yürütürler:

async function foo() {
  console.log("--one");
  console.log("--two");
}

console.log("start");
foo();
console.log("end");

async function foo() {
  console.log("--one");
  await 0; //just satisfy await with an expression
  console.log("--two");
}

console.log("start");
foo();
console.log("end");

Yani, bilmemiz gereken ilk şey , fonksiyonun geri kalanının daha sonra yürütülmesini awaitsağlayacaktır . Verilen örnekte bu , senkron kodun geri kalanından sonra yürütüleceği anlamına gelir . Bunun nedeni, mevcut olay döngüsü bittikten sonra herhangi bir Sözün çözülmesidir.console.log('x1 :', x)

Bu nedenle, neden ilkx2 : 1 giriş yaptığımızı ve neden ikinci giriş yaptığımızı açıklıyoruz , ancak ikinci değer neden değil . Mantıken olmalı burada ... ama ikinci yakalamak olduğunu anahtar kelime - bu olacak duraklatma zaten çalıştığını önce fonksiyonunun yürütülmesine başka bir şey. aslında şu şekilde işlenecekx2 : 55x += await 55awaitx += await 5

  1. Değerini alın x. İnfaz zamanında, bu 0.
  2. awaitsonraki ifade olan 5. Böylece, işlev şimdi duraklatılır ve daha sonra devam ettirilir.
  3. İşlevi sürdürün. İfade 5 olarak çözümlenir.
  4. 1. değerini ve 2/3 ifadesini ekleyin: 0 + 5
  5. Değeri 4'ten atayın. x

Yani, ondan sonra fonksiyon duraklamalar okumak xolduğunu 0ve zaten değişti zaman, ancak değerini yeniden okumaz devam x.

Eğer bunu yürütülecek eşdeğerin awaitiçine açarsak Promise, sizde:

let x = 0;                        // 1 declaring and assigning x

async function test() {           // 2 function declaration
    const temp = x;               // 4 value read of x
    await 0; //fake await to pause for demo
    return new Promise((resolve) => {
      x = temp + 5;               // 7 assign to x
      console.log('x1 :', x);     // 8 printing
      resolve();
    });
}

test();                           // 3 invoking the function
x += 1;                           // 5 assigning x
console.log('x2 :', x);           // 6 printing


3

Ya onun biraz zor aslında ne oluyor her iki toplama işlemleri parellaly oluyor yani operasyon şöyle olurdu:

Söz içinde: x += await 5==> x = x + await 5==> x = 0 + await 5==>5

Dışında: x += 1==> x = x + 1==> x = 0 + 1==>1

yukarıdaki tüm işlemler soldan sağa gerçekleştiği için, eklemenin ilk kısmı aynı anda hesaplanabilir ve 5'ten önce bir bekleme olduğundan bu katkı maddesi biraz gecikebilir. Kodun içine kesme noktası koyarak yürütmeyi görebilirsiniz.


1

Async ve Await, vaatlerin uzantısıdır. Bir eşzamansız işlevi, eşzamansız işlevinin yürütülmesini duraklatan ve iletilen Promise'ın çözünürlüğünü bekleyen ve ardından eşzamansız işlevinin yürütülmesini sürdüren ve çözümlenen değeri döndüren bir bekleme ifadesi içerebilir. Unutmayın, await anahtar sözcüğü yalnızca zaman uyumsuz işlevler için geçerlidir.

Sınama işlevini çağırdıktan sonra x değerini değiştirmiş olsanız bile, x değeri 0 olarak kalacaktır, çünkü zaman uyumsuzluk işlevi yeni örneğini oluşturmuştur. Bunun anlamı, değişkenin dışındaki her şeyin değişmesi çağrıldıktan sonra içindeki değeri değiştirmeyecektir. Artışınızı test fonksiyonunun üzerine koymazsanız.


" Değişken üzerindeki her şeyin değişmesi anlamı, çağrıldıktan sonra içindeki değeri değiştirmeyecektir ": bu doğru değil. Zaman uyumsuz işlevleri yapmak onların yürütme sırasında değişken değişiklikleri almaz. Sadece şunu deneyin: let x="Didn't receive change"; (async()=>{await 'Nothing'; console.log(x); await new Promise(resolve=>setTimeout(resolve,2000)); console.log(x)})(); x='Received synchronous change'; setTimeout(()=>{x='Received change'},1000)Çıktılar Received synchronous changeveReceived change
FZs
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.