เหตุใดตัวแปรของฉันจึงไม่เปลี่ยนแปลงหลังจากที่ฉันแก้ไขภายในฟังก์ชัน - การอ้างอิงรหัสอะซิงโครนัส

May 15 2014

จากตัวอย่างต่อไปนี้เหตุใดจึงouterScopeVarไม่ได้กำหนดไว้ในทุกกรณี

var outerScopeVar;

var img = document.createElement('img');
img.onload = function() {
    outerScopeVar = this.width;
};
img.src = 'lolcat.png';
alert(outerScopeVar);

var outerScopeVar;
setTimeout(function() {
    outerScopeVar = 'Hello Asynchronous World!';
}, 0);
alert(outerScopeVar);

// Example using some jQuery
var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
});
alert(outerScopeVar);

// Node.js example
var outerScopeVar;
fs.readFile('./catdog.html', function(err, data) {
    outerScopeVar = data;
});
console.log(outerScopeVar);

// with promises
var outerScopeVar;
myPromise.then(function (response) {
    outerScopeVar = response;
});
console.log(outerScopeVar);

// geolocation API
var outerScopeVar;
navigator.geolocation.getCurrentPosition(function (pos) {
    outerScopeVar = pos;
});
console.log(outerScopeVar);

เหตุใดจึงแสดงผลundefinedในตัวอย่างเหล่านี้ทั้งหมด ฉันไม่ต้องการวิธีแก้ปัญหาฉันต้องการทราบสาเหตุที่เกิดขึ้น


หมายเหตุ:นี่เป็นคำถามที่ยอมรับสำหรับasynchronicity JavaScript อย่าลังเลที่จะปรับปรุงคำถามนี้และเพิ่มตัวอย่างที่ง่ายขึ้นซึ่งชุมชนสามารถระบุได้

คำตอบ

605 FabrícioMatté May 15 2014 at 06:55

หนึ่งคำตอบ: asynchronicity

คำทำนาย

หัวข้อนี้ได้รับการทำซ้ำอย่างน้อยสองสามพันครั้งที่นี่ใน Stack Overflow ดังนั้นก่อนอื่นฉันต้องการชี้ให้เห็นแหล่งข้อมูลที่มีประโยชน์อย่างยิ่ง:


คำตอบสำหรับคำถามที่อยู่ในมือ

ลองติดตามพฤติกรรมทั่วไปก่อน ในตัวอย่างที่outerScopeVarมีการปรับเปลี่ยนภายในของฟังก์ชั่น ฟังก์ชั่นนั้นไม่ได้ดำเนินการทันทีอย่างชัดเจนมันกำลังถูกกำหนดหรือส่งผ่านเป็นอาร์กิวเมนต์ นั่นคือสิ่งที่เราเรียกว่าการเรียกกลับ

ตอนนี้คำถามคือโทรกลับเมื่อไหร่?

มันขึ้นอยู่กับกรณี ลองติดตามพฤติกรรมทั่วไปอีกครั้ง:

  • img.onloadอาจเรียกได้ว่าในอนาคตเมื่อ (และถ้า) โหลดภาพสำเร็จ
  • setTimeoutอาจจะเรียกว่าบางครั้งในอนาคตclearTimeoutหลังจากที่ล่าช้าได้หมดอายุลงและหมดเวลายังไม่ได้ถูกยกเลิกโดย หมายเหตุ: แม้ว่าจะใช้0เป็นแบบหน่วงเวลาเบราว์เซอร์ทั้งหมดจะมีค่าหน่วงเวลาการหมดเวลาขั้นต่ำ (ระบุเป็น 4ms ในข้อกำหนด HTML5)
  • $.postอาจมีการเรียกกลับของjQuery ในบางครั้งในอนาคตเมื่อ (และถ้า) การร้องขอ Ajax เสร็จสมบูรณ์
  • fs.readFileอาจมีการเรียกNode.js ในอนาคตเมื่อไฟล์ถูกอ่านสำเร็จหรือเกิดข้อผิดพลาด

ในทุกกรณีเรามีการเรียกกลับซึ่งอาจเรียกใช้บางครั้งในอนาคต นี้ "บางครั้งในอนาคต" คือสิ่งที่เราเรียกว่าการไหลไม่ตรงกัน

การดำเนินการแบบอะซิงโครนัสถูกผลักออกจากโฟลว์ซิงโครนัส นั่นคือรหัสอะซิงโครนัสจะไม่ดำเนินการในขณะที่สแต็กโค้ดซิงโครนัสกำลังดำเนินการ นี่คือความหมายของ JavaScript ที่เป็นเธรดเดียว

โดยเฉพาะอย่างยิ่งเมื่อเอ็นจิ้น JS ไม่ได้ใช้งาน - ไม่เรียกใช้สแต็กของ (a) โค้ดซิงโครนัส - มันจะสำรวจเหตุการณ์ที่อาจทำให้เกิดการเรียกกลับแบบอะซิงโครนัส (เช่นการหมดเวลาหมดอายุการตอบสนองของเครือข่ายที่ได้รับ) และดำเนินการทีละรายการ นี้ถือได้ว่าเป็นเหตุการณ์ห่วง

นั่นคือรหัสอะซิงโครนัสที่ไฮไลต์ในรูปทรงสีแดงที่วาดด้วยมือสามารถดำเนินการได้หลังจากที่โค้ดซิงโครนัสที่เหลือทั้งหมดในบล็อกโค้ดที่เกี่ยวข้องได้ดำเนินการ:

ในระยะสั้นฟังก์ชันการโทรกลับถูกสร้างขึ้นพร้อมกัน แต่ดำเนินการแบบอะซิงโครนัส คุณไม่สามารถพึ่งพาการทำงานของฟังก์ชันอะซิงโครนัสได้จนกว่าคุณจะรู้ว่าได้ดำเนินการแล้วและจะทำอย่างไร

มันเป็นเรื่องง่ายจริงๆ ตรรกะที่ขึ้นอยู่กับการเรียกใช้ฟังก์ชันอะซิงโครนัสควรเริ่มต้น / เรียกใช้จากภายในฟังก์ชันอะซิงโครนัสนี้ ตัวอย่างเช่นการย้ายalerts และconsole.logs เกินไปภายในฟังก์ชันเรียกกลับจะให้ผลลัพธ์ที่คาดหวังเนื่องจากผลลัพธ์มีอยู่ ณ จุดนั้น

ใช้ตรรกะการเรียกกลับของคุณเอง

บ่อยครั้งที่คุณต้องทำสิ่งต่างๆมากขึ้นด้วยผลลัพธ์จากฟังก์ชันอะซิงโครนัสหรือทำสิ่งต่างๆกับผลลัพธ์ขึ้นอยู่กับตำแหน่งที่เรียกใช้ฟังก์ชันอะซิงโครนัส มาจัดการกับตัวอย่างที่ซับซ้อนขึ้นเล็กน้อย:

var outerScopeVar;
helloCatAsync();
alert(outerScopeVar);

function helloCatAsync() {
    setTimeout(function() {
        outerScopeVar = 'Nya';
    }, Math.random() * 2000);
}

หมายเหตุ:ผมใช้setTimeoutที่มีความล่าช้าสุ่มเป็นฟังก์ชั่นไม่ตรงกันทั่วไปเช่นเดียวกับที่ใช้กับอาแจ็กซ์readFile, onloadและการไหลตรงกันอื่น ๆ

ตัวอย่างนี้เห็นได้ชัดว่ามีปัญหาเดียวกันกับตัวอย่างอื่น ๆ ไม่ต้องรอจนกว่าฟังก์ชันอะซิงโครนัสจะดำเนินการ

มาจัดการมันโดยใช้ระบบโทรกลับของเราเอง ก่อนอื่นเรากำจัดสิ่งที่น่าเกลียดouterScopeVarซึ่งไม่มีประโยชน์อย่างสมบูรณ์ในกรณีนี้ จากนั้นเราเพิ่มพารามิเตอร์ที่ยอมรับอาร์กิวเมนต์ของฟังก์ชันการเรียกกลับของเรา เมื่อการดำเนินการแบบอะซิงโครนัสเสร็จสิ้นเราจะเรียกการเรียกกลับนี้ว่าส่งผ่านผลลัพธ์ การใช้งาน (โปรดอ่านความคิดเห็นตามลำดับ):

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    alert(result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as argument from the helloCatAsync call
function helloCatAsync(callback) {
    // 3. Start async operation:
    setTimeout(function() {
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}

ข้อมูลโค้ดของตัวอย่างข้างต้น:

// 1. Call helloCatAsync passing a callback function,
//    which will be called receiving the result from the async operation
console.log("1. function called...")
helloCatAsync(function(result) {
    // 5. Received the result from the async function,
    //    now do whatever you want with it:
    console.log("5. result is: ", result);
});

// 2. The "callback" parameter is a reference to the function which
//    was passed as argument from the helloCatAsync call
function helloCatAsync(callback) {
    console.log("2. callback here is the function passed as argument above...")
    // 3. Start async operation:
    setTimeout(function() {
    console.log("3. start async operation...")
    console.log("4. finished async operation, calling the callback, passing the result...")
        // 4. Finished async operation,
        //    call the callback passing the result as argument
        callback('Nya');
    }, Math.random() * 2000);
}

บ่อยที่สุดในกรณีการใช้งานจริง DOM API และไลบรารีส่วนใหญ่มีฟังก์ชันการเรียกกลับอยู่แล้ว (การhelloCatAsyncใช้งานในตัวอย่างสาธิตนี้) คุณจะต้องส่งผ่านฟังก์ชันการโทรกลับและเข้าใจว่ามันจะดำเนินการจากโฟลว์ซิงโครนัสและปรับโครงสร้างโค้ดของคุณเพื่อรองรับสิ่งนั้น

นอกจากนี้คุณจะสังเกตเห็นว่าเนื่องจากลักษณะอะซิงโครนัสจึงเป็นไปไม่ได้ที่returnค่าจากโฟลว์อะซิงโครนัสกลับไปยังโฟลว์ซิงโครนัสที่มีการกำหนดการโทรกลับเนื่องจากการเรียกกลับแบบอะซิงโครนัสจะดำเนินการนานหลังจากที่โค้ดซิงโครนัสเสร็จสิ้นแล้ว

แทนที่จะreturnใช้ค่าจากการโทรกลับแบบอะซิงโครนัสคุณจะต้องใช้รูปแบบการโทรกลับหรือ ... สัญญา

สัญญา

แม้ว่าจะมีหลายวิธีในการป้องกันการเรียกกลับด้วย vanilla JS แต่สัญญากำลังได้รับความนิยมเพิ่มขึ้นและกำลังได้รับมาตรฐานใน ES6 (ดูPromise - MDN )

สัญญา (aka Futures) ให้ความเป็นเชิงเส้นมากขึ้นและด้วยเหตุนี้การอ่านโค้ดแบบอะซิงโครนัสจึงเป็นที่น่าพอใจ แต่การอธิบายฟังก์ชันการทำงานทั้งหมดไม่อยู่ในขอบเขตของคำถามนี้ แต่ฉันจะทิ้งแหล่งข้อมูลที่ยอดเยี่ยมเหล่านี้ไว้ให้ผู้สนใจ:


เนื้อหาสำหรับอ่านเพิ่มเติมเกี่ยวกับความไม่แน่นอนของ JavaScript

  • Art of Node - Callbacksอธิบายโค้ดแบบอะซิงโครนัสและการเรียกกลับได้เป็นอย่างดีด้วยตัวอย่าง vanilla JS และโค้ด Node.js เช่นกัน

หมายเหตุ:ฉันทำเครื่องหมายคำตอบนี้เป็น Community Wiki ดังนั้นทุกคนที่มีชื่อเสียงอย่างน้อย 100 คนสามารถแก้ไขและปรับปรุงได้! โปรดอย่าลังเลที่จะปรับปรุงคำตอบนี้หรือส่งคำตอบใหม่ทั้งหมดหากคุณต้องการเช่นกัน

ฉันต้องการเปลี่ยนคำถามนี้ให้เป็นหัวข้อที่ยอมรับได้เพื่อตอบปัญหาความไม่สัมพันธ์กันซึ่งไม่เกี่ยวข้องกับ Ajax (มีวิธีการตอบกลับจากการโทร AJAX อย่างไร ) ดังนั้นหัวข้อนี้จึงต้องการความช่วยเหลือจากคุณเพื่อให้ดีและเป็นประโยชน์มากที่สุด !

158 Matt May 29 2014 at 16:09

คำตอบของFabrícioคือจุดที่; แต่ฉันต้องการเติมเต็มคำตอบของเขาด้วยเทคนิคที่น้อยกว่าซึ่งมุ่งเน้นไปที่การเปรียบเทียบเพื่อช่วยอธิบายแนวคิดของความไม่สัมพันธ์กัน


อะนาล็อก ...

เมื่อวานงานที่ฉันกำลังทำต้องการข้อมูลบางอย่างจากเพื่อนร่วมงาน ฉันเรียกเขาขึ้น; นี่คือวิธีการสนทนา:

ฉัน : สวัสดีบ๊อบผมจำเป็นต้องรู้วิธีการที่เราfoo 'งบาร์ ' วันที่สัปดาห์ที่ผ่านมา จิมต้องการรายงานเรื่องนี้และคุณเป็นคนเดียวที่รู้รายละเอียดเกี่ยวกับเรื่องนี้

Bob : แน่นอน แต่ฉันจะใช้เวลาประมาณ 30 นาที?

ฉัน : บ๊อบที่ยอดเยี่ยม คืนแหวนให้ฉันเมื่อคุณมีข้อมูล!

เมื่อถึงจุดนี้ฉันวางสายโทรศัพท์ เนื่องจากฉันต้องการข้อมูลจาก Bob เพื่อทำรายงานให้เสร็จฉันจึงออกจากรายงานและไปหากาแฟแทนจากนั้นฉันก็พบกับอีเมลบางฉบับ 40 นาทีต่อมา (บ๊อบทำงานช้า) บ็อบโทรกลับมาและให้ข้อมูลที่ฉันต้องการ ณ จุดนี้ฉันกลับมาทำงานกับรายงานของฉันต่อเนื่องจากฉันมีข้อมูลทั้งหมดที่ต้องการ


ลองนึกภาพว่าบทสนทนาเป็นแบบนี้แทนหรือไม่

ฉัน : สวัสดีบ๊อบผมจำเป็นต้องรู้วิธีการที่เราfoo 'งบาร์ ' วันที่สัปดาห์ที่ผ่านมา จิมต้องการเป็นรายงานและคุณเป็นคนเดียวที่รู้รายละเอียดเกี่ยวกับเรื่องนี้

Bob : แน่นอน แต่ฉันจะใช้เวลาประมาณ 30 นาที?

ฉัน : บ๊อบที่ยอดเยี่ยม ฉันจะรอ.

และฉันก็นั่งรอ และรอ. และรอ. เป็นเวลา 40 นาที ไม่ทำอะไรเลยนอกจากรอ ในที่สุดบ็อบก็ให้ข้อมูลแก่ฉันเราวางสายและฉันก็ทำรายงานเสร็จ แต่ฉันสูญเสียผลผลิตไป 40 นาที


นี่คือพฤติกรรมแบบอะซิงโครนัสและซิงโครนัส

นี่คือสิ่งที่เกิดขึ้นในตัวอย่างทั้งหมดในคำถามของเรา การโหลดรูปภาพการโหลดไฟล์จากดิสก์และการขอเพจผ่าน AJAX ล้วนเป็นการดำเนินการที่ช้า (ในบริบทของการประมวลผลสมัยใหม่)

แทนที่จะรอให้การดำเนินการที่ช้าเหล่านี้เสร็จสมบูรณ์ JavaScript ให้คุณลงทะเบียนฟังก์ชันการเรียกกลับซึ่งจะดำเนินการเมื่อการดำเนินการช้าเสร็จสิ้น อย่างไรก็ตามในระหว่างนี้ JavaScript จะรันโค้ดอื่น ๆ ต่อไป ความจริงที่ว่า JavaScript รันรหัสอื่น ๆในขณะที่รอการดำเนินการช้าที่จะสมบูรณ์ทำให้ลักษณะการทำงานไม่ตรงกัน หาก JavaScript รอจนกว่าการดำเนินการจะเสร็จสมบูรณ์ก่อนที่จะเรียกใช้โค้ดอื่น ๆ สิ่งนี้จะเป็นพฤติกรรมซิงโครนั

var outerScopeVar;    
var img = document.createElement('img');

// Here we register the callback function.
img.onload = function() {
    // Code within this function will be executed once the image has loaded.
    outerScopeVar = this.width;
};

// But, while the image is loading, JavaScript continues executing, and
// processes the following lines of JavaScript.
img.src = 'lolcat.png';
alert(outerScopeVar);

ในโค้ดด้านบนเราขอให้ JavaScript โหลดlolcat.pngซึ่งเป็นการดำเนินการที่สโลว์ไลฟ์ ฟังก์ชันเรียกกลับจะทำงานเมื่อการดำเนินการช้านี้เสร็จสิ้น แต่ในระหว่างนี้ JavaScript จะประมวลผลโค้ดบรรทัดถัดไป กล่าวคือalert(outerScopeVar).

นี่คือเหตุผลที่เราเห็นการแจ้งเตือนแสดงundefinedขึ้น เนื่องจากalert()ประมวลผลทันทีแทนที่จะโหลดภาพ

ในการแก้ไขรหัสของเราสิ่งที่เราต้องทำคือย้ายalert(outerScopeVar)รหัสไปที่ฟังก์ชันเรียกกลับ ด้วยเหตุนี้เราจึงไม่ต้องการouterScopeVarตัวแปรที่ประกาศเป็นตัวแปรส่วนกลางอีกต่อไป

var img = document.createElement('img');

img.onload = function() {
    var localScopeVar = this.width;
    alert(localScopeVar);
};

img.src = 'lolcat.png';

คุณจะมักจะเห็นการเรียกกลับมีการระบุเป็นฟังก์ชั่นเนื่องจากว่าเป็นเพียงวิธี * ใน JavaScript เพื่อกำหนดรหัสบางส่วน แต่ไม่รันมันจนกระทั่งต่อมา

ดังนั้นในตัวอย่างทั้งหมดของเราfunction() { /* Do something */ }คือการเรียกกลับ แก้ไขทั้งหมดตัวอย่างทั้งหมดที่เราต้องทำคือการย้ายโค้ดที่ต้องการการตอบสนองของการดำเนินการเข้าไปในที่นั่น!

* ในทางเทคนิคคุณสามารถใช้ได้eval()เช่นกัน แต่eval()เป็นสิ่งที่ชั่วร้ายสำหรับวัตถุประสงค์นี้


ฉันจะให้ผู้โทรรอได้อย่างไร

ขณะนี้คุณอาจมีรหัสบางอย่างที่คล้ายกับสิ่งนี้

function getWidthOfImage(src) {
    var outerScopeVar;

    var img = document.createElement('img');
    img.onload = function() {
        outerScopeVar = this.width;
    };
    img.src = src;
    return outerScopeVar;
}

var width = getWidthOfImage('lolcat.png');
alert(width);

อย่างไรก็ตามตอนนี้เรารู้แล้วว่าสิ่งนี้return outerScopeVarเกิดขึ้นทันที ก่อนที่onloadฟังก์ชันเรียกกลับจะอัปเดตตัวแปร สิ่งนี้นำไปสู่การgetWidthOfImage()กลับมาundefinedและundefinedการแจ้งเตือน

ในการแก้ไขปัญหานี้เราต้องอนุญาตให้ฟังก์ชันที่เรียกgetWidthOfImage()ใช้ลงทะเบียนการโทรกลับจากนั้นย้ายการแจ้งเตือนของความกว้างให้อยู่ในการเรียกกลับนั้น

function getWidthOfImage(src, cb) {     
    var img = document.createElement('img');
    img.onload = function() {
        cb(this.width);
    };
    img.src = src;
}

getWidthOfImage('lolcat.png', function (width) {
    alert(width);
});

... เช่นเดิมโปรดทราบว่าเราสามารถลบตัวแปรส่วนกลางได้ (ในกรณีนี้width)

75 JohnnyHK Jan 21 2015 at 06:42

นี่เป็นคำตอบที่กระชับยิ่งขึ้นสำหรับผู้ที่กำลังมองหาข้อมูลอ้างอิงอย่างรวดเร็วรวมถึงตัวอย่างบางส่วนที่ใช้คำสัญญาและ async / await

เริ่มต้นด้วยวิธีไร้เดียงสา (ที่ไม่ได้ผล) สำหรับฟังก์ชันที่เรียกใช้เมธอดแบบอะซิงโครนัส (ในกรณีนี้setTimeout) และส่งกลับข้อความ:

function getMessage() {
  var outerScopeVar;
  setTimeout(function() {
    outerScopeVar = 'Hello asynchronous world!';
  }, 0);
  return outerScopeVar;
}
console.log(getMessage());

undefinedได้รับการบันทึกไว้ในกรณีนี้เพราะgetMessageผลตอบแทนก่อนที่จะโทรกลับเรียกว่าและการปรับปรุงsetTimeoutouterScopeVar

สองวิธีหลักในการแก้ปัญหาคือการใช้การโทรกลับและสัญญา :

โทรกลับ

การเปลี่ยนแปลงในที่นี้คือgetMessageยอมรับcallbackพารามิเตอร์ที่จะถูกเรียกเพื่อส่งผลลัพธ์กลับไปยังรหัสการโทรเมื่อพร้อมใช้งาน

function getMessage(callback) {
  setTimeout(function() {
    callback('Hello asynchronous world!');
  }, 0);
}
getMessage(function(message) {
  console.log(message);
});

สัญญา

คำมั่นสัญญาเป็นทางเลือกที่ยืดหยุ่นกว่าการโทรกลับเนื่องจากสามารถรวมเข้าด้วยกันเพื่อประสานการทำงานแบบไม่ซิงค์หลายรายการ สัญญา / A +การดำเนินงานมาตรฐานมีให้กำเนิดใน Node.js (0.12+) และเบราว์เซอร์ในปัจจุบันหลายคน แต่ยังถูกนำมาใช้ในห้องสมุดเช่นครามและQ

function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello asynchronous world!');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);  
});

jQuery รอการตัดบัญชี

jQuery มีฟังก์ชันการทำงานที่คล้ายกับสัญญาที่รอการตัดบัญชี

function getMessage() {
  var deferred = $.Deferred();
  setTimeout(function() {
    deferred.resolve('Hello asynchronous world!');
  }, 0);
  return deferred.promise();
}

getMessage().done(function(message) {
  console.log(message);  
});

async / รอ

หากสภาพแวดล้อม JavaScript ของคุณรองรับasyncและawait(เช่น Node.js 7.6+) คุณสามารถใช้คำสัญญาพร้อมกันภายในasyncฟังก์ชัน:

function getMessage () {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve('Hello asynchronous world!');
        }, 0);
    });
}

async function main() {
    let message = await getMessage();
    console.log(message);
}

main();
58 JohannesFahrenkrug Dec 08 2015 at 23:48

outerScopeVarที่จะระบุชัดเจนถ้วยหมาย

ฟังก์ชันอะซิงโครนัสเป็นเหมือน ...

14 Teja Feb 26 2016 at 10:59

คำตอบอื่น ๆ นั้นยอดเยี่ยมและฉันแค่ต้องการให้คำตอบตรงไปตรงมาสำหรับเรื่องนี้ เพียง จำกัด การโทรแบบอะซิงโครนัส jQuery

การโทรของ ajax ทั้งหมด (รวมถึง$.getหรือ$.postหรือ$.ajax) เป็นแบบอะซิงโครนัส

พิจารณาตัวอย่างของคุณ

var outerScopeVar;  //line 1
$.post('loldog', function(response) {  //line 2
    outerScopeVar = response;
});
alert(outerScopeVar);  //line 3

การเรียกใช้รหัสเริ่มต้นจากบรรทัดที่ 1 ประกาศตัวแปรและทริกเกอร์และการเรียกแบบอะซิงโครนัสในบรรทัดที่ 2 (กล่าวคือคำขอโพสต์) และดำเนินการต่อจากบรรทัดที่ 3 โดยไม่ต้องรอให้คำขอโพสต์ดำเนินการให้เสร็จสิ้น

สมมติว่าคำขอโพสต์ใช้เวลา 10 วินาทีในการดำเนินการให้เสร็จสิ้นค่าของouterScopeVarจะถูกตั้งค่าหลังจาก 10 วินาทีนั้นเท่านั้น

เพื่อทดลองใช้

var outerScopeVar; //line 1
$.post('loldog', function(response) {  //line 2, takes 10 seconds to complete
    outerScopeVar = response;
});
alert("Lets wait for some time here! Waiting is fun");  //line 3
alert(outerScopeVar);  //line 4

ตอนนี้เมื่อคุณดำเนินการนี้คุณจะได้รับการแจ้งเตือนทางบรรทัดที่ 3 ตอนนี้รอสักครู่จนกว่าคุณจะแน่ใจว่าคำขอโพสต์ได้ส่งคืนค่าบางส่วน จากนั้นเมื่อคุณคลิกตกลงบนกล่องการแจ้งเตือนการแจ้งเตือนครั้งถัดไปจะพิมพ์ค่าที่คาดไว้เนื่องจากคุณรอมัน

ในสถานการณ์จริงรหัสจะกลายเป็น

var outerScopeVar;
$.post('loldog', function(response) {
    outerScopeVar = response;
    alert(outerScopeVar);
});

รหัสทั้งหมดที่ขึ้นอยู่กับการโทรแบบอะซิงโครนัสจะถูกย้ายภายในบล็อกอะซิงโครนัสหรือโดยการรอสายแบบอะซิงโครนัส

11 TomSebastian Oct 27 2015 at 13:35

ในสถานการณ์เหล่านี้ทั้งหมดouterScopeVarจะถูกแก้ไขหรือกำหนดค่าแบบอะซิงโครนัสหรือเกิดขึ้นในเวลาต่อมา (รอหรือฟังเหตุการณ์บางอย่างที่จะเกิดขึ้น) ซึ่งการดำเนินการปัจจุบันจะไม่รอดังนั้นทุกกรณีขั้นตอนการดำเนินการปัจจุบันจึงส่งผลให้outerScopeVar = undefined

มาดูตัวอย่างแต่ละตัวอย่างกัน (ฉันทำเครื่องหมายส่วนที่เรียกว่าอะซิงโครนัสหรือล่าช้าเพื่อให้เหตุการณ์บางอย่างเกิดขึ้น):

1.

ที่นี่เราลงทะเบียน eventlistner ซึ่งจะดำเนินการตามเหตุการณ์นั้น ๆ ที่นี่กำลังโหลดรูปภาพจากนั้นการดำเนินการปัจจุบันจะต่อเนื่องกับบรรทัดถัดไปimg.src = 'lolcat.png';และในalert(outerScopeVar);ขณะเดียวกันเหตุการณ์อาจไม่เกิดขึ้น กล่าวคือ funtion img.onloadรอให้ภาพที่อ้างถึงโหลดโดยไม่ตรงกัน สิ่งนี้จะเกิดขึ้นตามตัวอย่างทั้งหมด - เหตุการณ์อาจแตกต่างกัน

2.

เหตุการณ์การหมดเวลามีบทบาทที่นี่ซึ่งจะเรียกตัวจัดการหลังจากเวลาที่กำหนด นี่คือ0แต่มันยังคงลงทะเบียนเหตุการณ์แบบอะซิงโครนัสซึ่งจะถูกเพิ่มไปยังตำแหน่งสุดท้ายของการEvent Queueดำเนินการซึ่งทำให้รับประกันความล่าช้า

3.

ครั้งนี้ ajax โทรกลับ

4.

โหนดสามารถพิจารณาได้ว่าเป็นราชาของการเข้ารหัสแบบอะซิงโครนัสที่นี่ฟังก์ชันที่ทำเครื่องหมายไว้จะถูกลงทะเบียนเป็นตัวจัดการการโทรกลับซึ่งจะดำเนินการหลังจากอ่านไฟล์ที่ระบุ

5.

สัญญาที่ชัดเจน (สิ่งที่จะทำในอนาคต) เป็นแบบอะซิงโครนัส ดูอะไรคือความแตกต่างระหว่างรอการตัดบัญชีสัญญาและอนาคตใน JavaScript?

https://www.quora.com/Whats-the-difference-between-a-promise-and-a-callback-in-Javascript