เหตุใดตัวแปรของฉันจึงไม่เปลี่ยนแปลงหลังจากที่ฉันแก้ไขภายในฟังก์ชัน - การอ้างอิงรหัสอะซิงโครนัส
จากตัวอย่างต่อไปนี้เหตุใดจึง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 อย่าลังเลที่จะปรับปรุงคำถามนี้และเพิ่มตัวอย่างที่ง่ายขึ้นซึ่งชุมชนสามารถระบุได้
คำตอบ
หนึ่งคำตอบ: asynchronicity
คำทำนาย
หัวข้อนี้ได้รับการทำซ้ำอย่างน้อยสองสามพันครั้งที่นี่ใน Stack Overflow ดังนั้นก่อนอื่นฉันต้องการชี้ให้เห็นแหล่งข้อมูลที่มีประโยชน์อย่างยิ่ง:
คำตอบของ @Felix Kling สำหรับ "ฉันจะตอบกลับการตอบกลับจากการโทรแบบอะซิงโครนัสได้อย่างไร" . ดูคำตอบที่ยอดเยี่ยมของเขาที่อธิบายโฟลว์ซิงโครนัสและอะซิงโครนัสตลอดจนส่วน "รหัสปรับโครงสร้าง"
@Benjamin Gruenbaum ได้ใช้ความพยายามอย่างมากในการอธิบายความไม่สม่ำเสมอในหัวข้อเดียวกันคำตอบของ @Matt Esch สำหรับ "รับข้อมูลจาก fs.readFile"ยังอธิบายถึงความไม่สม่ำเสมอได้เป็นอย่างดีในลักษณะง่ายๆ
คำตอบสำหรับคำถามที่อยู่ในมือ
ลองติดตามพฤติกรรมทั่วไปก่อน ในตัวอย่างที่outerScopeVar
มีการปรับเปลี่ยนภายในของฟังก์ชั่น ฟังก์ชั่นนั้นไม่ได้ดำเนินการทันทีอย่างชัดเจนมันกำลังถูกกำหนดหรือส่งผ่านเป็นอาร์กิวเมนต์ นั่นคือสิ่งที่เราเรียกว่าการเรียกกลับ
ตอนนี้คำถามคือโทรกลับเมื่อไหร่?
มันขึ้นอยู่กับกรณี ลองติดตามพฤติกรรมทั่วไปอีกครั้ง:
img.onload
อาจเรียกได้ว่าในอนาคตเมื่อ (และถ้า) โหลดภาพสำเร็จsetTimeout
อาจจะเรียกว่าบางครั้งในอนาคตclearTimeout
หลังจากที่ล่าช้าได้หมดอายุลงและหมดเวลายังไม่ได้ถูกยกเลิกโดย หมายเหตุ: แม้ว่าจะใช้0
เป็นแบบหน่วงเวลาเบราว์เซอร์ทั้งหมดจะมีค่าหน่วงเวลาการหมดเวลาขั้นต่ำ (ระบุเป็น 4ms ในข้อกำหนด HTML5)$.post
อาจมีการเรียกกลับของjQuery ในบางครั้งในอนาคตเมื่อ (และถ้า) การร้องขอ Ajax เสร็จสมบูรณ์fs.readFile
อาจมีการเรียกNode.js ในอนาคตเมื่อไฟล์ถูกอ่านสำเร็จหรือเกิดข้อผิดพลาด
ในทุกกรณีเรามีการเรียกกลับซึ่งอาจเรียกใช้บางครั้งในอนาคต นี้ "บางครั้งในอนาคต" คือสิ่งที่เราเรียกว่าการไหลไม่ตรงกัน
การดำเนินการแบบอะซิงโครนัสถูกผลักออกจากโฟลว์ซิงโครนัส นั่นคือรหัสอะซิงโครนัสจะไม่ดำเนินการในขณะที่สแต็กโค้ดซิงโครนัสกำลังดำเนินการ นี่คือความหมายของ JavaScript ที่เป็นเธรดเดียว
โดยเฉพาะอย่างยิ่งเมื่อเอ็นจิ้น JS ไม่ได้ใช้งาน - ไม่เรียกใช้สแต็กของ (a) โค้ดซิงโครนัส - มันจะสำรวจเหตุการณ์ที่อาจทำให้เกิดการเรียกกลับแบบอะซิงโครนัส (เช่นการหมดเวลาหมดอายุการตอบสนองของเครือข่ายที่ได้รับ) และดำเนินการทีละรายการ นี้ถือได้ว่าเป็นเหตุการณ์ห่วง
นั่นคือรหัสอะซิงโครนัสที่ไฮไลต์ในรูปทรงสีแดงที่วาดด้วยมือสามารถดำเนินการได้หลังจากที่โค้ดซิงโครนัสที่เหลือทั้งหมดในบล็อกโค้ดที่เกี่ยวข้องได้ดำเนินการ:
ในระยะสั้นฟังก์ชันการโทรกลับถูกสร้างขึ้นพร้อมกัน แต่ดำเนินการแบบอะซิงโครนัส คุณไม่สามารถพึ่งพาการทำงานของฟังก์ชันอะซิงโครนัสได้จนกว่าคุณจะรู้ว่าได้ดำเนินการแล้วและจะทำอย่างไร
มันเป็นเรื่องง่ายจริงๆ ตรรกะที่ขึ้นอยู่กับการเรียกใช้ฟังก์ชันอะซิงโครนัสควรเริ่มต้น / เรียกใช้จากภายในฟังก์ชันอะซิงโครนัสนี้ ตัวอย่างเช่นการย้ายalert
s และconsole.log
s เกินไปภายในฟังก์ชันเรียกกลับจะให้ผลลัพธ์ที่คาดหวังเนื่องจากผลลัพธ์มีอยู่ ณ จุดนั้น
ใช้ตรรกะการเรียกกลับของคุณเอง
บ่อยครั้งที่คุณต้องทำสิ่งต่างๆมากขึ้นด้วยผลลัพธ์จากฟังก์ชันอะซิงโครนัสหรือทำสิ่งต่างๆกับผลลัพธ์ขึ้นอยู่กับตำแหน่งที่เรียกใช้ฟังก์ชันอะซิงโครนัส มาจัดการกับตัวอย่างที่ซับซ้อนขึ้นเล็กน้อย:
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 อย่างไร ) ดังนั้นหัวข้อนี้จึงต้องการความช่วยเหลือจากคุณเพื่อให้ดีและเป็นประโยชน์มากที่สุด !
คำตอบของ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
)
นี่เป็นคำตอบที่กระชับยิ่งขึ้นสำหรับผู้ที่กำลังมองหาข้อมูลอ้างอิงอย่างรวดเร็วรวมถึงตัวอย่างบางส่วนที่ใช้คำสัญญาและ async / await
เริ่มต้นด้วยวิธีไร้เดียงสา (ที่ไม่ได้ผล) สำหรับฟังก์ชันที่เรียกใช้เมธอดแบบอะซิงโครนัส (ในกรณีนี้setTimeout
) และส่งกลับข้อความ:
function getMessage() {
var outerScopeVar;
setTimeout(function() {
outerScopeVar = 'Hello asynchronous world!';
}, 0);
return outerScopeVar;
}
console.log(getMessage());
undefined
ได้รับการบันทึกไว้ในกรณีนี้เพราะgetMessage
ผลตอบแทนก่อนที่จะโทรกลับเรียกว่าและการปรับปรุงsetTimeout
outerScopeVar
สองวิธีหลักในการแก้ปัญหาคือการใช้การโทรกลับและสัญญา :
โทรกลับ
การเปลี่ยนแปลงในที่นี้คือ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();
outerScopeVar
ที่จะระบุชัดเจนถ้วยหมาย
ฟังก์ชันอะซิงโครนัสเป็นเหมือน ...
คำตอบอื่น ๆ นั้นยอดเยี่ยมและฉันแค่ต้องการให้คำตอบตรงไปตรงมาสำหรับเรื่องนี้ เพียง จำกัด การโทรแบบอะซิงโครนัส 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);
});
รหัสทั้งหมดที่ขึ้นอยู่กับการโทรแบบอะซิงโครนัสจะถูกย้ายภายในบล็อกอะซิงโครนัสหรือโดยการรอสายแบบอะซิงโครนัส
ในสถานการณ์เหล่านี้ทั้งหมดouterScopeVar
จะถูกแก้ไขหรือกำหนดค่าแบบอะซิงโครนัสหรือเกิดขึ้นในเวลาต่อมา (รอหรือฟังเหตุการณ์บางอย่างที่จะเกิดขึ้น) ซึ่งการดำเนินการปัจจุบันจะไม่รอดังนั้นทุกกรณีขั้นตอนการดำเนินการปัจจุบันจึงส่งผลให้outerScopeVar = undefined
มาดูตัวอย่างแต่ละตัวอย่างกัน (ฉันทำเครื่องหมายส่วนที่เรียกว่าอะซิงโครนัสหรือล่าช้าเพื่อให้เหตุการณ์บางอย่างเกิดขึ้น):
1.
ที่นี่เราลงทะเบียน eventlistner ซึ่งจะดำเนินการตามเหตุการณ์นั้น ๆ ที่นี่กำลังโหลดรูปภาพจากนั้นการดำเนินการปัจจุบันจะต่อเนื่องกับบรรทัดถัดไปimg.src = 'lolcat.png';
และในalert(outerScopeVar);
ขณะเดียวกันเหตุการณ์อาจไม่เกิดขึ้น กล่าวคือ funtion img.onload
รอให้ภาพที่อ้างถึงโหลดโดยไม่ตรงกัน สิ่งนี้จะเกิดขึ้นตามตัวอย่างทั้งหมด - เหตุการณ์อาจแตกต่างกัน
2.
เหตุการณ์การหมดเวลามีบทบาทที่นี่ซึ่งจะเรียกตัวจัดการหลังจากเวลาที่กำหนด นี่คือ0
แต่มันยังคงลงทะเบียนเหตุการณ์แบบอะซิงโครนัสซึ่งจะถูกเพิ่มไปยังตำแหน่งสุดท้ายของการEvent Queue
ดำเนินการซึ่งทำให้รับประกันความล่าช้า
3.
4.
โหนดสามารถพิจารณาได้ว่าเป็นราชาของการเข้ารหัสแบบอะซิงโครนัสที่นี่ฟังก์ชันที่ทำเครื่องหมายไว้จะถูกลงทะเบียนเป็นตัวจัดการการโทรกลับซึ่งจะดำเนินการหลังจากอ่านไฟล์ที่ระบุ
5.
สัญญาที่ชัดเจน (สิ่งที่จะทำในอนาคต) เป็นแบบอะซิงโครนัส ดูอะไรคือความแตกต่างระหว่างรอการตัดบัญชีสัญญาและอนาคตใน JavaScript?
https://www.quora.com/Whats-the-difference-between-a-promise-and-a-callback-in-Javascript