Fungsi async dengan + =

63

let x = 0;

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

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

Nilai-nilai yang xdicatat adalah 1dan 5. Pertanyaan saya adalah: mengapa nilai x 5pada log kedua?

Jika testdieksekusi setelah x += 1(karena ini adalah fungsi async) maka nilai x adalah 1 pada saat testdieksekusi, jadi x += await 5harus membuat nilai x 6.

ALDRIN P VINCENT
sumber
1
Anda harus tahu perbedaan antara await (x += 5) dan x += await 5.
Singhi John

Jawaban:

60

TL; DR: Karena +=membaca xsebelumnya, tetapi menulisnya setelah itu berubah, karena awaitkata kunci dalam operan kedua (sisi kanan).


asyncfungsi berjalan secara sinkron ketika mereka dipanggil hingga awaitpernyataan pertama .

Jadi, jika Anda menghapus await, berperilaku seperti fungsi normal (dengan pengecualian bahwa itu masih mengembalikan Janji).

Jika demikian, Anda masuk 5dan 6masuk ke konsol:

let x = 0;

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

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

Yang pertama awaitberhenti berjalan secara sinkron, meskipun argumennya tersedia secara sinkron, sehingga yang berikut akan kembali 1dan 6, seperti yang Anda harapkan:

let x = 0;

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

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

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

Namun, kasing Anda sedikit lebih rumit.

Anda telah memasukkan awaitekspresi yang digunakan +=.

Anda mungkin tahu, bahwa di JS x += yidentik dengan x = (x + y). Saya akan menggunakan formulir yang terakhir untuk pemahaman yang lebih baik:

let x = 0;

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

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

Ketika penerjemah mencapai garis ini ...

x = (x + await 5);

... mulai mengevaluasinya, dan ternyata menjadi ...

x = (0 + await 5);

... lalu, ia mencapai awaitdan berhenti.

Kode setelah panggilan fungsi mulai berjalan, dan mengubah nilai x, lalu mencatatnya.

xsekarang 1.

Kemudian, setelah skrip utama keluar, interpreter kembali ke testfungsi yang dijeda , dan terus mengevaluasi baris itu:

x = (0 + 5);

Dan, karena nilai xsudah diganti, itu tetap 0.

Akhirnya, penafsir melakukan penambahan, toko 5untuk x, dan log itu.

Anda dapat memeriksa perilaku ini dengan masuk ke dalam pengambil / penyetel properti objek (dalam contoh ini, y.zmencerminkan nilai dari 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;
}

FZ
sumber
Mungkin perlu diperhatikan: "Anda mungkin tahu, itu x += yidentik dengan x = (x + y)." - Ini bukan kasus dalam setiap situasi dalam setiap bahasa, tetapi secara umum Anda dapat mengandalkan mereka bertindak sama.
Nick
11

Pernyataan Anda x += await 5berkeinginan untuk

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

Nilai _temporary adalah 0, dan jika Anda berubah xselama await(yang kode Anda lakukan) itu tidak masalah, itu akan diberikan 5setelahnya.

Bergi
sumber
9

Kode ini cukup rumit untuk diikuti karena dibutuhkan beberapa lompatan async yang tak terduga. Mari kita periksa (dekat) bagaimana itu sebenarnya akan dieksekusi dan saya akan menjelaskan mengapa setelah itu. Saya juga mengubah log konsol untuk menambahkan nomor - membuatnya lebih mudah merujuk dan juga menunjukkan apa yang dicatat:

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

Jadi, kode itu tidak benar-benar berjalan lurus, itu sudah pasti. Dan kami juga memiliki 4/7hal yang aneh . Dan itu benar-benar keseluruhan masalah di sini.

Pertama-tama, mari kita perjelas - fungsi async sebenarnya tidak sepenuhnya asinkron. Mereka hanya akan menghentikan eksekusi dan melanjutkan nanti jika awaitkata kunci digunakan. Tanpa itu, mereka mengeksekusi dari atas ke bawah, ekspresi demi ekspresi secara serempak:

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");

Jadi, hal pertama yang perlu kita ketahui bahwa menggunakan awaitakan membuat sisa fungsi dieksekusi nanti. Dalam contoh yang diberikan, itu berarti bahwa console.log('x1 :', x)akan dieksekusi setelah sisa kode sinkron. Itu karena setiap Janji akan diselesaikan setelah loop acara saat ini selesai.

Jadi, ini menjelaskan mengapa kita x2 : 1login terlebih dahulu dan mengapa x2 : 5login kedua tetapi tidak mengapa nilai terakhir 5. Secara logis x += await 5seharusnya 5... tetapi di sini adalah tangkapan kedua untuk awaitkata kunci - itu akan menghentikan sementara pelaksanaan fungsi tetapi apa pun sebelum sudah berjalan. x += await 5sebenarnya akan diproses dengan cara berikut

  1. Ambil nilai x. Pada saat eksekusi, itu 0.
  2. awaitungkapan selanjutnya yaitu 5. Jadi, fungsinya berhenti sekarang dan akan dilanjutkan lagi nanti.
  3. Lanjutkan fungsinya. Ekspresi diselesaikan sebagai 5.
  4. Tambahkan nilai dari 1. dan ekspresi dari 2/3: 0 + 5
  5. Tetapkan nilai dari 4. hingga x

Jadi, jeda fungsi setelah membaca bahwa xadalah 0dan dilanjutkan bila itu sudah berubah, bagaimanapun, tidak membaca kembali nilai x.

Jika kita membuka bungkusan tersebut awaitke dalam Promisesetara yang akan mengeksekusi, Anda harus:

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

VLAZ
sumber
3

Ya itu sedikit rumit apa yang sebenarnya terjadi adalah kedua operasi penambahan terjadi parellaly sehingga operasi akan seperti:

Dalam janji: x += await 5==> x = x + await 5==> x = 0 + await 5==>5

Di luar: x += 1==> x = x + 1==> x = 0 + 1==>1

karena semua operasi di atas terjadi dari kiri ke kanan, bagian pertama dari penambahan dapat dihitung pada waktu yang sama dan karena ada menunggu sebelum 5 yang additio dapat menunda sedikit. Anda dapat melihat eksekusi dengan meletakkan breakpoint di dalam kode.

Pranav C Balan
sumber
1

Async dan Await adalah perpanjangan dari janji. Fungsi async dapat berisi ekspresi menunggu yang menjeda eksekusi fungsi async dan menunggu resolusi Promise yang disahkan, dan kemudian melanjutkan eksekusi fungsi async dan mengembalikan nilai yang diselesaikan. Ingat, kata kunci yang menunggu hanya valid di dalam fungsi async.

Bahkan jika Anda telah mengubah nilai x setelah memanggil fungsi tes, masih nilai x akan tetap 0 karena fungsi async telah dibuat itu contoh baru. Berarti semuanya berubah pada variabel di luarnya tidak akan mengubah nilai di dalamnya setelah dipanggil. Kecuali jika Anda menempatkan kenaikan Anda di atas fungsi tes.

Qonvex620
sumber
" Berarti semuanya berubah pada variabel di luarnya tidak akan mengubah nilai di dalamnya setelah dipanggil ": itu tidak benar. Fungsi - fungsi Async menerima perubahan variabel selama eksekusi mereka. Coba saja ini: 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)Ini output Received synchronous changedanReceived change
FZ