+ = के साथ Async फ़ंक्शन


63

let x = 0;

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

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

xलॉग के मान हैं 1और 5। मेरा सवाल है: x 5दूसरे लॉग पर मूल्य क्यों है ?

यदि testबाद क्रियान्वित किया जाता है x += 1(क्योंकि यह एक async समारोह है) तो एक्स का मान द्वारा समय 1 है testनिष्पादित किया जाता है, इसलिए x += await 5का मूल्य बनाना चाहिए x 6


1
आपको await (x += 5) और के बीच का अंतर पता होना चाहिए x += await 5
सिंघी जॉन

जवाबों:


60

TL; DR: क्योंकि पहले +=पढ़ता है x, लेकिन इसे बदलने के बाद इसे लिखता है, awaitइसके दूसरे ऑपरेंड में कीवर्ड के कारण (राइट-हैंड साइड)।


asyncजब वे पहले awaitबयान तक बुलाए जाते हैं तो फ़ंक्शन सिंक्रोनस रूप से चलते हैं ।

इसलिए, यदि आप हटाते हैं await, तो यह एक सामान्य फ़ंक्शन (अपवाद के साथ व्यवहार करता है कि यह अभी भी एक वादा करता है)।

उस स्थिति में, आप प्राप्त करते हैं 5और 6कंसोल में हैं:

let x = 0;

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

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

पहला awaitसिंक्रोनस रनिंग बंद कर देता है, भले ही उसका तर्क समकालिक रूप से उपलब्ध हो, इसलिए निम्न वापसी होगी 1और 6, जैसा कि आप उम्मीद करते हैं:

let x = 0;

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

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

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

हालाँकि, आपका मामला थोड़ा अधिक जटिल है।

आपने awaitएक अभिव्यक्ति के अंदर रखा है, जो उपयोग करता है +=

तुम्हें शायद पता है, कि जेएस x += yमें समान है x = (x + y)। मैं बेहतर समझ के लिए बाद के फॉर्म का उपयोग करूंगा:

let x = 0;

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

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

जब दुभाषिया इस रेखा तक पहुँचता है ...

x = (x + await 5);

... इसका मूल्यांकन शुरू होता है, और यह ...

x = (0 + await 5);

... तो, यह तक पहुँचता है awaitऔर बंद हो जाता है।

फ़ंक्शन कॉल के बाद कोड चलने लगता है, और मान को संशोधित करता है x, फिर उसे लॉग करता है।

xअब है 1

फिर, मुख्य स्क्रिप्ट से बाहर निकलने के बाद, दुभाषिया वापस चल testसमारोह में जाता है, और उस रेखा का मूल्यांकन जारी रखता है:

x = (0 + 5);

और, चूंकि मूल्य xको पहले से प्रतिस्थापित किया गया है, इसलिए यह बना हुआ है 0

अंत में, दुभाषिया अलावा करता है, दुकानों 5के लिए x, और यह लॉग करता है।

आप किसी ऑब्जेक्ट गुण गेट्टर / सेटर के अंदर लॉग इन करके इस व्यवहार की जाँच कर सकते हैं (इस उदाहरण में, y.zइसका मान दर्शाता है 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;
}


शायद ध्यान देने योग्य: "आप शायद जानते हैं, x += yयह समान है x = (x + y)।" - यह हर स्थिति में हर भाषा में नहीं होता है, लेकिन सामान्य तौर पर आप उन पर अभिनय कर सकते हैं।
निक

11

अपने बयान x += await 5के लिए desugars

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

_tempOrary मूल्य है 0, और आप के परिवर्तित होने xके दौरान await(जो आपके कोड करता है) यह बात यह सौंप दिया जाता है नहीं है, 5बाद में।


9

यह कोड फॉलो करने के लिए काफी जटिल है क्योंकि यह कुछ अनपेक्षित एसिंक्स को आगे-पीछे करता है। आइए जांच करें (पास) कि यह वास्तव में कैसे निष्पादित होने जा रहा है और मैं समझाऊंगा कि बाद में क्यों। मैंने एक नंबर जोड़ने के लिए कंसोल लॉग्स को भी बदल दिया है - उन्हें संदर्भित करना आसान बनाता है और यह भी बेहतर दिखाता है कि लॉग क्या है:

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

तो, कोड वास्तव में सीधे तरीके से नहीं चल रहा है, यह सुनिश्चित करने के लिए है। और हमारे पास एक अजीब 4/7बात भी है। और यह वास्तव में यहाँ समस्या की संपूर्णता है।

सबसे पहले, आइए स्पष्ट करें - async फ़ंक्शन वास्तव में कड़ाई से अतुल्यकालिक नहीं हैं । वे केवल निष्पादन को रोकते हैं और बाद में फिर से शुरू करते हैं यदि awaitकीवर्ड का उपयोग किया जाता है। इसके बिना, वे ऊपर से नीचे तक निष्पादित करते हैं, अभिव्यक्ति के बाद अभिव्यक्ति तुल्यकालिक:

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

तो, पहली बात हमें यह जानना चाहिए कि उपयोग awaitकरने से बाकी फ़ंक्शन बाद में निष्पादित होंगे। दिए गए उदाहरण में, इसका मतलब है कि console.log('x1 :', x)निष्पादित किया जा रहा है शेष सिंक्रोनस कोड के बाद । ऐसा इसलिए है क्योंकि किसी भी वादे को वर्तमान ईवेंट लूप के पूरा होने के बाद हल किया जाएगा।

तो, यह बताता है कि हम पहलेx2 : 1 लॉग इन क्यों करते हैं और दूसरा लॉग क्यों किया जाता है, लेकिन बाद का मूल्य क्यों नहीं है । तार्किक रूप से होना चाहिए ... लेकिन यहां कीवर्ड की दूसरी पकड़ है - यह फ़ंक्शन के निष्पादन को रोक देगा लेकिन इससे पहले कि यह पहले ही चल चुका है।x2 : 55x += await 55awaitx += await 5वास्तव में निम्नलिखित तरीके से संसाधित किया जा रहा है

  1. का मान प्राप्त करें x। निष्पादन के समय, यह0
  2. await अगली अभिव्यक्ति जो है 5 । इसलिए, कार्य अब रुक गया है और बाद में फिर से शुरू किया जाएगा।
  3. फ़ंक्शन को फिर से शुरू करें। अभिव्यक्ति 5 के रूप में हल हो गई है।
  4. 1 से मान जोड़ें और 2/3 से अभिव्यक्ति: 0 + 5
  5. 4. से मान निर्दिष्ट करें x

इसलिए, यह पढ़ने के बाद फ़ंक्शन रुक जाता xहै 0और फिर से शुरू होने पर इसे फिर से शुरू कर देता है, हालांकि, यह मान को फिर से नहीं पढ़ता है x

यदि हम निष्पादित किए awaitजाने वाले Promiseसमतुल्य को खोल देते हैं , तो आपके पास है:

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

हां इसकी थोड़ी सी मुश्किल यह है कि वास्तव में क्या हो रहा है, दोनों अतिरिक्त संचालन पेरेलाइल हो रहे हैं, इसलिए ऑपरेशन इस तरह होगा:

वादा के भीतर: x += await 5==> x = x + await 5==> x = 0 + await 5==>5

बाहर: x += 1==> x = x + 1==> x = 0 + 1==>1

चूंकि उपरोक्त सभी ऑपरेशन बाएं से दाएं हो रहे हैं, इसलिए इसके अलावा इसके पहले भाग की गणना एक ही समय में की जा सकती है और चूंकि 5 से पहले प्रतीक्षा हो रही है कि एडिटियो में थोड़ी देरी हो सकती है। आप कोड के भीतर ब्रेकपॉइंट लगाकर निष्पादन को देख सकते हैं।


1

Async और Await वादों के विस्तार हैं। एक async फ़ंक्शन में एक प्रतीक्षित अभिव्यक्ति हो सकती है जो async फ़ंक्शन के निष्पादन को रोकती है और पास किए गए प्रॉमिस के रिज़ॉल्यूशन की प्रतीक्षा करती है, और फिर async फ़ंक्शन के निष्पादन को फिर से शुरू करता है और हल किए गए मान को वापस करता है। याद रखें, प्रतीक्षित कीवर्ड केवल async फ़ंक्शन के अंदर मान्य है।

भले ही आपने परीक्षण फ़ंक्शन को कॉल करने के बाद x का मान बदल दिया हो, फिर भी x का मान 0 बना रहेगा क्योंकि async फ़ंक्शन ने पहले ही नया उदाहरण बना लिया है। मतलब इसके बाहर चर पर सब कुछ बदल जाता है, इसे कॉल किए जाने के बाद इसके अंदर का मूल्य नहीं बदलेगा। जब तक कि आप अपनी इन्क्रीमेंट को टेस्ट फंक्शन से ऊपर न रखें।


"इसका अर्थ है कि इसके बाहर चर पर सब कुछ बदल जाता है, इसे कहा जाने के बाद इसके अंदर का मूल्य नहीं बदलेगा ": यह सच नहीं है। Async फ़ंक्शन उनके निष्पादन के दौरान परिवर्तनशील परिवर्तन प्राप्त करते हैं। बस यह प्रयास करें: 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)यह आउटपुट Received synchronous changeऔरReceived change
FZs
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.