ลองนึกภาพว่าคุณเป็นนักร้องชั้นนำ แล้วแฟนๆ ก็ถามหาเพลงใหม่ของคุณทั้งวันทั้งคืน
เพื่อบรรเทาทุกข์ คุณสัญญาว่าจะส่งไปให้พวกเขาเมื่อมีการเผยแพร่ คุณให้รายชื่อแฟนๆ ของคุณ พวกเขาสามารถกรอกที่อยู่อีเมลของตน เพื่อว่าเมื่อเพลงพร้อมให้ใช้งาน ทุกฝ่ายที่สมัครรับข้อมูลจะได้รับทันที และแม้ว่าจะมีอะไรผิดพลาดร้ายแรง เช่น ไฟไหม้ในสตูดิโอ ซึ่งคุณไม่สามารถเผยแพร่เพลงได้ พวกเขาจะยังคงได้รับการแจ้งเตือน
ทุกคนมีความสุข ทั้งคุณ เพราะผู้คนไม่รุมคุณอีกต่อไป และแฟนๆ เพราะพวกเขาจะไม่พลาดเพลงนี้
นี่เป็นการเปรียบเทียบในชีวิตจริงกับสิ่งที่เรามักมีในการเขียนโปรแกรม:
“รหัสการผลิต” ที่ทำบางสิ่งบางอย่างและต้องใช้เวลา ตัวอย่างเช่น โค้ดบางตัวที่โหลดข้อมูลผ่านเครือข่าย นั่นก็คือ “นักร้อง”
“โค้ดที่ใช้งาน” ที่ต้องการผลลัพธ์ของ “โค้ดการผลิต” เมื่อพร้อมแล้ว ฟังก์ชันหลายอย่างอาจต้องการผลลัพธ์นั้น เหล่านี้คือ "แฟนคลับ"
คำสัญญา คืออ็อบเจ็กต์ JavaScript พิเศษที่เชื่อมโยง "โค้ดการผลิต" และ "โค้ดที่ใช้งาน" เข้าด้วยกัน ในแง่ของการเปรียบเทียบ: นี่คือ "รายการสมัครสมาชิก" “รหัสการผลิต” ใช้เวลาใดก็ตามที่จำเป็นในการสร้างผลลัพธ์ตามที่สัญญาไว้ และ “คำสัญญา” จะทำให้ผลลัพธ์นั้นพร้อมใช้งานสำหรับรหัสที่สมัครเป็นสมาชิกทั้งหมดเมื่อพร้อม
การเปรียบเทียบนั้นไม่ถูกต้องมากนัก เนื่องจากสัญญาของ JavaScript นั้นซับซ้อนกว่ารายการสมัครสมาชิกทั่วไป: พวกมันมีคุณสมบัติและข้อจำกัดเพิ่มเติม แต่เริ่มต้นก็ดีแล้ว
ไวยากรณ์ตัวสร้างสำหรับวัตถุสัญญาคือ:
ให้สัญญา = สัญญาใหม่ (ฟังก์ชั่น (แก้ไข, ปฏิเสธ) { // ผู้ดำเนินการ (รหัสการผลิต "นักร้อง") -
ฟังก์ชันที่ส่งผ่านไปยัง new Promise
เรียกว่า executor เมื่อมีการสร้าง new Promise
ผู้ดำเนินการจะทำงานโดยอัตโนมัติ มันมีรหัสการผลิตซึ่งควรจะสร้างผลลัพธ์ในที่สุด ในแง่ของการเปรียบเทียบข้างต้น ผู้ดำเนินการคือ "นักร้อง"
อาร์กิวเมนต์ resolve
และ reject
เป็นการเรียกกลับที่จัดทำโดย JavaScript เอง รหัสของเราอยู่ในตัวดำเนินการเท่านั้น
เมื่อผู้ดำเนินการได้รับผลลัพธ์ ไม่ว่าจะเร็วหรือช้า ไม่สำคัญ ก็ควรเรียกหนึ่งในการโทรกลับเหล่านี้:
resolve(value)
— หากงานเสร็จสิ้นสำเร็จ โดยมี value
ผลลัพธ์
reject(error)
— หากมีข้อผิดพลาดเกิดขึ้น error
จะเป็นวัตถุข้อผิดพลาด
โดยสรุป: ตัวดำเนินการจะทำงานโดยอัตโนมัติและพยายามทำงาน เมื่อดำเนินการเสร็จแล้ว ระบบจะเรียก resolve
หากสำเร็จหรือ reject
หากมีข้อผิดพลาด
อ็อบเจ็กต์ promise
ที่ส่งคืนโดยตัวสร้าง new Promise
มีคุณสมบัติภายในเหล่านี้:
state
— เริ่มแรกเป็น "pending"
จากนั้นเปลี่ยนเป็น "fulfilled"
เมื่อเรียก resolve
หรือ "rejected"
เมื่อถูกเรียก reject
result
— เริ่มแรก undefined
จากนั้นเปลี่ยนเป็น value
เมื่อเรียกใช้ resolve(value)
หรือเกิด error
เมื่อเรียก reject(error)
ในที่สุดผู้ดำเนินการก็ย้าย promise
ไปยังสถานะใดสถานะหนึ่งเหล่านี้:
ต่อไปเราจะมาดูกันว่า “แฟนๆ” สามารถสมัครรับการเปลี่ยนแปลงเหล่านี้ได้อย่างไร
นี่คือตัวอย่างของตัวสร้างสัญญาและฟังก์ชันตัวดำเนินการอย่างง่ายที่มี "การสร้างโค้ด" ที่ต้องใช้เวลา (ผ่าน setTimeout
):
ให้สัญญา = สัญญาใหม่ (ฟังก์ชั่น (แก้ไข, ปฏิเสธ) { // ฟังก์ชั่นจะดำเนินการโดยอัตโนมัติเมื่อมีการสร้างสัญญา // หลังจากผ่านไป 1 วินาทีสัญญาณว่างานเสร็จสิ้นพร้อมกับผลลัพธ์ "เสร็จสิ้น" setTimeout(() => แก้ไข ("เสร็จสิ้น"), 1,000); -
เราเห็นสองสิ่งได้โดยการรันโค้ดด้านบน:
ผู้ดำเนินการจะถูกเรียกโดยอัตโนมัติและทันที (โดย new Promise
)
ผู้ดำเนินการได้รับอาร์กิวเมนต์สองข้อ: resolve
และ reject
ฟังก์ชันเหล่านี้ถูกกำหนดไว้ล่วงหน้าโดยเอ็นจิ้น JavaScript ดังนั้นเราจึงไม่จำเป็นต้องสร้างมันขึ้นมา เราควรโทรหาหนึ่งในนั้นเมื่อพร้อมเท่านั้น
หลังจาก "ประมวลผล" หนึ่งวินาที ผู้ดำเนินการจะเรียก resolve("done")
เพื่อสร้างผลลัพธ์ สิ่งนี้จะเปลี่ยนสถานะของวัตถุ promise
:
นั่นคือตัวอย่างความสำเร็จของงาน ซึ่งก็คือ "คำมั่นสัญญาที่ปฏิบัติตาม"
และตอนนี้เป็นตัวอย่างของผู้ดำเนินการที่ปฏิเสธสัญญาโดยมีข้อผิดพลาด:
ให้สัญญา = สัญญาใหม่ (ฟังก์ชั่น (แก้ไข, ปฏิเสธ) { // หลังจาก 1 วินาทีส่งสัญญาณว่างานเสร็จสิ้นโดยมีข้อผิดพลาด setTimeout(() => ปฏิเสธ (ข้อผิดพลาดใหม่ ("ขออภัย!")), 1,000); -
การเรียกร้องให้ reject(...)
ย้ายวัตถุสัญญาไปที่สถานะ "rejected"
:
โดยสรุป ผู้ดำเนินการควรทำงาน (โดยปกติเป็นสิ่งที่ต้องใช้เวลา) จากนั้นจึงเรียก resolve
หรือ reject
เพื่อเปลี่ยนสถานะของอ็อบเจ็กต์สัญญาที่เกี่ยวข้อง
คำสัญญาที่ได้รับการแก้ไขหรือปฏิเสธเรียกว่า "ตกลง" ซึ่งตรงข้ามกับสัญญา "รอดำเนินการ" ในตอนแรก
อาจมีเพียงผลลัพธ์เดียวหรือข้อผิดพลาด
ผู้ดำเนินการควรเรียกเพียงหนึ่ง resolve
หรือ reject
หนึ่งครั้ง การเปลี่ยนแปลงสถานะใด ๆ ถือเป็นที่สิ้นสุด
การเรียก resolve
และ reject
เพิ่มเติมทั้งหมดจะถูกละเว้น:
ให้สัญญา = สัญญาใหม่ (ฟังก์ชั่น (แก้ไข, ปฏิเสธ) { แก้ไข ("เสร็จสิ้น"); ปฏิเสธ(ข้อผิดพลาดใหม่("…")); //ละเลย setTimeout(() => แก้ไข ("…")); //ละเลย -
แนวคิดก็คืองานที่ผู้ดำเนินการทำอาจมีผลลัพธ์เดียวหรือมีข้อผิดพลาด
นอกจากนี้ resolve
/ reject
คาดหวังเพียงอาร์กิวเมนต์เดียว (หรือไม่มีเลย) และจะเพิกเฉยต่ออาร์กิวเมนต์เพิ่มเติม
ปฏิเสธวัตถุที่ Error
ในกรณีที่มีสิ่งผิดปกติเกิดขึ้น ผู้ดำเนินการควรเรียก reject
ที่สามารถทำได้ด้วยการโต้แย้งประเภทใดก็ได้ (เช่นเดียวกับ resolve
) แต่ขอแนะนำให้ใช้วัตถุ Error
(หรือวัตถุที่สืบทอดมาจาก Error
) เหตุผลนั้นก็จะปรากฏชัดในไม่ช้า
โทร resolve
/ reject
ทันที
ในทางปฏิบัติ ตัวดำเนินการมักจะทำอะไรบางอย่างแบบอะซิงโครนัสและเรียก resolve
/ reject
หลังจากผ่านไประยะหนึ่ง แต่ก็ไม่จำเป็นต้องทำ นอกจากนี้เรายังสามารถเรียก resolve
หรือ reject
ได้ทันทีเช่นนี้
ให้สัญญา = สัญญาใหม่ (ฟังก์ชั่น (แก้ไข, ปฏิเสธ) { //ไม่สละเวลาไปทำงาน. แก้ไข (123); // ให้ผลลัพธ์ทันที: 123 -
ตัวอย่างเช่น สิ่งนี้อาจเกิดขึ้นเมื่อเราเริ่มทำงานแต่กลับเห็นว่าทุกอย่างเสร็จสิ้นและแคชไว้แล้ว
ไม่เป็นไร. เรามีคำสัญญาที่ได้รับการแก้ไขทันที
state
และ result
อยู่ภายใน
state
คุณสมบัติและ result
ของวัตถุ Promise เป็นแบบภายใน เราไม่สามารถเข้าถึงได้โดยตรง เราสามารถใช้วิธีการ .then
/ .catch
/ .finally
เพื่อสิ่งนั้น มีการอธิบายไว้ด้านล่าง
วัตถุ Promise ทำหน้าที่เป็นตัวเชื่อมโยงระหว่างผู้ดำเนินการ ("รหัสการผลิต" หรือ "นักร้อง") และฟังก์ชันการบริโภค ("แฟนๆ") ซึ่งจะได้รับผลลัพธ์หรือข้อผิดพลาด ฟังก์ชั่นการบริโภคสามารถลงทะเบียน (สมัครสมาชิก) โดยใช้วิธี .then
และ .catch
สิ่งพื้นฐานที่สำคัญที่สุดคือ . .then
ไวยากรณ์คือ:
สัญญา.แล้ว( function(result) { /* จัดการผลลัพธ์ที่สำเร็จ */ }, ฟังก์ชั่น(ข้อผิดพลาด) { /* จัดการข้อผิดพลาด */ } -
อาร์กิวเมนต์แรกของ .then
คือฟังก์ชันที่ทำงานเมื่อสัญญาได้รับการแก้ไขและได้รับผลลัพธ์
อาร์กิวเมนต์ที่สองของ .then
คือฟังก์ชันที่ทำงานเมื่อสัญญาถูกปฏิเสธและได้รับข้อผิดพลาด
ตัวอย่างเช่น ต่อไปนี้เป็นปฏิกิริยาต่อคำสัญญาที่แก้ไขได้สำเร็จ:
ให้สัญญา = สัญญาใหม่ (ฟังก์ชั่น (แก้ไข, ปฏิเสธ) { setTimeout(() => แก้ไข ("เสร็จสิ้น!"), 1,000); - // solve รันฟังก์ชันแรกใน .then สัญญา.แล้ว( result => alert(result), // แสดงว่า "เสร็จสิ้น!" หลังจากผ่านไป 1 วินาที error => alert(error) // ไม่ทำงาน -
ฟังก์ชันแรกถูกดำเนินการ
และในกรณีที่ถูกปฏิเสธ ประการที่สอง:
ให้สัญญา = สัญญาใหม่ (ฟังก์ชั่น (แก้ไข, ปฏิเสธ) { setTimeout(() => ปฏิเสธ (ข้อผิดพลาดใหม่ ("ขออภัย!")), 1,000); - // reject รันฟังก์ชันที่สองใน .then สัญญา.แล้ว( result => alert(result), // ไม่ทำงาน error => alert(error) // แสดง "Error: Whoops!" หลังจากผ่านไป 1 วินาที -
หากเราสนใจเฉพาะความสำเร็จเท่านั้น เราก็สามารถจัดเตรียมอาร์กิวเมนต์ฟังก์ชันได้เพียงอาร์กิวเมนต์เดียวให้กับ .then
:
ให้สัญญา = สัญญาใหม่ (แก้ไข => { setTimeout(() => แก้ไข ("เสร็จสิ้น!"), 1,000); - สัญญาแล้ว (แจ้งเตือน); // แสดงว่า "เสร็จสิ้น!" หลังจากผ่านไป 1 วินาที
หากเราสนใจเฉพาะข้อผิดพลาด เราสามารถใช้ null
เป็นอาร์กิวเมนต์แรกได้: .then(null, errorHandlingFunction)
หรือเราสามารถใช้ .catch(errorHandlingFunction)
ซึ่งเหมือนกันทุกประการ:
ให้สัญญา = สัญญาใหม่ ((แก้ไข, ปฏิเสธ) => { setTimeout(() => ปฏิเสธ (ข้อผิดพลาดใหม่ ("ขออภัย!")), 1,000); - // .catch(f) เหมือนกับ Promise.then(null, f) สัญญาจับ (แจ้งเตือน); // แสดง "ข้อผิดพลาด: อ๊ะ!" หลังจากผ่านไป 1 วินาที
การโทร .catch(f)
เป็นอะนาล็อกที่สมบูรณ์ของ .then(null, f)
มันเป็นเพียงชวเลข
เช่นเดียวกับที่มีประโยค finally
ใน try {...} catch {...}
เป็น finally
ก็มีคำสัญญา
การเรียก .finally(f)
คล้ายกับ .then(f, f)
ในแง่ที่ว่า f
จะทำงานเสมอ เมื่อสัญญาได้รับการแก้ไข: ไม่ว่าจะเป็นการแก้ไขหรือปฏิเสธ
แนวคิด finally
คือการตั้งค่าตัวจัดการสำหรับดำเนินการล้างข้อมูล/สรุปผลหลังจากการดำเนินการก่อนหน้านี้เสร็จสิ้น
เช่น ไฟแสดงการหยุดการโหลด การปิดการเชื่อมต่อที่ไม่จำเป็นอีกต่อไป เป็นต้น
คิดว่ามันเป็นการจบงานปาร์ตี้ ไม่ว่างานปาร์ตี้จะดีหรือไม่ดี มีเพื่อนกี่คน เรายังต้องการ (หรืออย่างน้อยควร) ทำความสะอาดหลังจากนั้น
รหัสอาจมีลักษณะดังนี้:
สัญญาใหม่ ((แก้ไข, ปฏิเสธ) => { /* ทำบางอย่างที่ต้องใช้เวลา จากนั้นจึงเรียกแก้ไขหรืออาจปฏิเสธ */ - // ทำงานเมื่อมีการตกลงตามสัญญาแล้วไม่สำคัญว่าจะสำเร็จหรือไม่ .finally(() => หยุดโหลดตัวบ่งชี้) // ดังนั้นตัวบ่งชี้การโหลดจะหยุดเสมอก่อนที่เราจะไปต่อ .then(result => แสดงผล, err => แสดงข้อผิดพลาด)
โปรดทราบว่า finally(f)
ไม่ใช่นามแฝงของ then(f,f)
อย่างแน่นอน
มีความแตกต่างที่สำคัญ:
finally
ตัวจัดการก็ไม่มีข้อโต้แย้ง ใน finally
เราก็ไม่รู้ว่าสัญญาจะสำเร็จหรือไม่ ไม่เป็นไร เนื่องจากงานของเรามักจะดำเนินการตามขั้นตอนการสรุปผล "ทั่วไป"
โปรดดูตัวอย่างด้านบน: อย่างที่คุณเห็น finally
ตัวจัดการก็ไม่มีข้อโต้แย้ง และผลลัพธ์ที่สัญญาไว้จะถูกจัดการโดยตัวจัดการคนถัดไป
finally
ตัวจัดการจะ "ผ่าน" ผลลัพธ์หรือข้อผิดพลาดไปยังตัวจัดการที่เหมาะสมถัดไป
ตัวอย่างเช่น ผลลัพธ์จะถูกส่งผ่านไป finally
then
:
สัญญาใหม่ ((แก้ไข, ปฏิเสธ) => { setTimeout(() => แก้ไข ("ค่า"), 2000); - .finally(() => alert("Promise ready")) // ทริกเกอร์ก่อน .then(result => alert(result)); // <-- .แล้วแสดง "ค่า"
อย่างที่คุณเห็น value
ที่ส่งคืนโดยสัญญาแรกจะถูกส่งผ่านไป finally
สัญญา then
ไปในที่สุด
สะดวกมาก เพราะ finally
ไม่ได้หมายถึงการประมวลผลผลลัพธ์ตามสัญญา ดังที่กล่าวไว้ มันเป็นสถานที่สำหรับทำความสะอาดทั่วไป ไม่ว่าผลลัพธ์จะเป็นอย่างไร
และนี่คือตัวอย่างของข้อผิดพลาด เพื่อให้เราดูว่า finally
มันผ่านไปอย่างไรเพื่อ catch
:
สัญญาใหม่ ((แก้ไข, ปฏิเสธ) => { โยนข้อผิดพลาดใหม่ ("ข้อผิดพลาด"); - .finally(() => alert("Promise ready")) // ทริกเกอร์ก่อน .catch(err => การแจ้งเตือน(err)); // <-- .catch แสดงข้อผิดพลาด
finally
ตัวจัดการก็ไม่ควรส่งคืนสิ่งใดเลย หากเป็นเช่นนั้น ค่าที่ส่งคืนจะถูกละเว้นโดยไม่แจ้งให้ทราบ
ข้อยกเว้นเดียวสำหรับกฎนี้คือเมื่อ finally
ตัวจัดการแสดงข้อผิดพลาด จากนั้นข้อผิดพลาดนี้จะไปที่ตัวจัดการถัดไป แทนที่จะเป็นผลลัพธ์ใดๆ ก่อนหน้านี้
สรุป:
finally
ตัวจัดการจะไม่ได้รับผลลัพธ์ของตัวจัดการก่อนหน้า (ไม่มีข้อโต้แย้ง) ผลลัพธ์นี้จะถูกส่งผ่านแทน ไปยังตัวจัดการที่เหมาะสมคนถัดไป
หาก finally
ตัวจัดการส่งคืนบางสิ่ง มันก็จะถูกละเว้น
เมื่อเกิดข้อผิดพลาด finally
การดำเนินการจะไปที่ตัวจัดการข้อผิดพลาดที่ใกล้ที่สุด
ฟีเจอร์เหล่านี้มีประโยชน์และทำให้สิ่งต่างๆ ทำงานได้อย่างถูกต้องหาก finally
เราใช้วิธีที่ควรจะใช้: สำหรับขั้นตอนการล้างข้อมูลทั่วไป
เราสามารถแนบตัวจัดการเข้ากับสัญญาที่ตกลงกันได้
หากสัญญาอยู่ระหว่างการพิจารณา . .then/catch/finally
handlers จะรอผลลัพธ์
บางครั้ง อาจเป็นไปได้ว่าสัญญาได้รับการชำระแล้วเมื่อเราเพิ่มตัวจัดการเข้าไป
ในกรณีเช่นนี้ ตัวจัดการเหล่านี้จะเริ่มทำงานทันที:
// คำสัญญาจะได้รับการแก้ไขทันทีเมื่อมีการสร้าง ให้สัญญา = สัญญาใหม่ (แก้ไข => แก้ไข ("เสร็จสิ้น!")); สัญญาแล้ว (แจ้งเตือน); // เสร็จแล้ว! (ปรากฏขึ้นในขณะนี้)
โปรดทราบว่าสิ่งนี้ทำให้คำสัญญามีประสิทธิภาพมากกว่าสถานการณ์ "รายการสมัครสมาชิก" ในชีวิตจริง หากนักร้องได้เปิดตัวเพลงของตนแล้วและมีผู้ลงทะเบียนในรายชื่อการสมัครรับข้อมูล พวกเขาอาจจะไม่ได้รับเพลงนั้น จะต้องสมัครสมาชิกในชีวิตจริงก่อนวันงาน
คำสัญญามีความยืดหยุ่นมากขึ้น เราสามารถเพิ่มตัวจัดการได้ตลอดเวลา: หากผลลัพธ์มีอยู่แล้ว พวกมันก็แค่ดำเนินการ
ต่อไป เรามาดูตัวอย่างที่เป็นประโยชน์เพิ่มเติมว่า Promises สามารถช่วยให้เราเขียนโค้ดแบบอะซิงโครนัสได้อย่างไร
เรามีฟังก์ชัน loadScript
สำหรับโหลดสคริปต์จากบทที่แล้ว
นี่คือรูปแบบที่เรียกกลับตามเพื่อเตือนเรา:
ฟังก์ชั่น loadScript (src, โทรกลับ) { ให้ script = document.createElement('script'); script.src = src; script.onload = () => โทรกลับ (null, สคริปต์); script.onerror = () => โทรกลับ (ข้อผิดพลาดใหม่ (`ข้อผิดพลาดในการโหลดสคริปต์สำหรับ ${src}`)); document.head.append(สคริปต์); -
มาเขียนมันใหม่โดยใช้สัญญา
ฟังก์ชั่นใหม่ loadScript
จะไม่ต้องการการโทรกลับ แต่จะสร้างและส่งคืนออบเจ็กต์ Promise ซึ่งจะแก้ไขเมื่อการโหลดเสร็จสมบูรณ์ โค้ดภายนอกสามารถเพิ่มตัวจัดการ (ฟังก์ชั่นสมัครสมาชิก) ได้โดยใช้ .then
:
ฟังก์ชั่น loadScript (src) { คืนสัญญาใหม่ (ฟังก์ชั่น (แก้ไข, ปฏิเสธ) { ให้ script = document.createElement('script'); script.src = src; script.onload = () => แก้ไข (สคริปต์); script.onerror = () => ปฏิเสธ (ข้อผิดพลาดใหม่ (`ข้อผิดพลาดในการโหลดสคริปต์สำหรับ ${src}`)); document.head.append(สคริปต์); - -
การใช้งาน:
ให้สัญญา = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"); สัญญา.แล้ว( script => alert(`${script.src} ถูกโหลดแล้ว!`), error => alert(`ข้อผิดพลาด: ${error.message}`) - PROMISE.then(script => alert('ตัวจัดการอื่น...'));
เราจะเห็นประโยชน์บางประการจากรูปแบบการโทรกลับได้ทันที:
สัญญา | โทรกลับ |
---|---|
คำสัญญาช่วยให้เราทำสิ่งต่าง ๆ ตามลำดับธรรมชาติ ขั้นแรก เราเรียกใช้ loadScript(script) และ .then จึงเขียนว่าจะทำอย่างไรกับผลลัพธ์ | เราต้องมีฟังก์ชัน callback เมื่อเรียกใช้ loadScript(script, callback) กล่าวอีกนัยหนึ่ง เราต้องรู้ว่าต้องทำอย่างไรกับผลลัพธ์ ก่อนที่ จะเรียก loadScript |
เราสามารถโทรหา .then ได้ตามสัญญากี่ครั้งก็ได้ตามที่เราต้องการ แต่ละครั้ง เราจะเพิ่ม "แฟน" ใหม่ ซึ่งเป็นฟังก์ชันการสมัครสมาชิกใหม่ ลงใน "รายการสมัครสมาชิก" ข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ในบทถัดไป: สัญญาผูกมัด | สามารถโทรกลับได้เพียงครั้งเดียว |
ดังนั้นคำมั่นสัญญาทำให้เรามีการไหลของโค้ดและความยืดหยุ่นที่ดีขึ้น แต่ยังมีอีกมาก เราจะเห็นสิ่งนั้นในบทต่อไป
ผลลัพธ์ของโค้ดด้านล่างคืออะไร?
ให้สัญญา = สัญญาใหม่ (ฟังก์ชั่น (แก้ไข, ปฏิเสธ) { แก้ไข (1); setTimeout(() => แก้ไข (2), 1,000); - สัญญาแล้ว (แจ้งเตือน);
ผลลัพธ์คือ: 1
การเรียกให้ resolve
ครั้งที่สองจะถูกละเว้น เนื่องจากจะพิจารณาเฉพาะการเรียก reject/resolve
ครั้งแรกเท่านั้น สายเพิ่มเติมจะถูกละเว้น
ฟังก์ชัน setTimeout
ในตัวใช้การโทรกลับ สร้างทางเลือกตามคำมั่นสัญญา
ฟังก์ชั่น delay(ms)
ควรส่งคืนสัญญา คำสัญญานั้นควรได้รับการแก้ไขหลังจาก ms
วินาที เพื่อให้เราสามารถเพิ่ม .then
เข้าไปได้ เช่นนี้
ฟังก์ชั่นล่าช้า (ms) { // รหัสของคุณ - ล่าช้า (3000). จากนั้น (() => การแจ้งเตือน ('ทำงานหลังจาก 3 วินาที'));
ฟังก์ชั่นล่าช้า (ms) { ส่งคืนสัญญาใหม่ (แก้ไข => setTimeout (แก้ไข, ms)); - ล่าช้า (3000). จากนั้น (() => การแจ้งเตือน ('ทำงานหลังจาก 3 วินาที'));
โปรดทราบว่าในงานนี้ resolve
จะถูกเรียกโดยไม่มีข้อโต้แย้ง เราจะไม่ส่งคืนค่าใด ๆ จาก delay
เพียงตรวจสอบให้แน่ใจว่าเกิดความล่าช้า
เขียนฟังก์ชัน showCircle
ใหม่ในโซลูชันของงาน แวดวงภาพเคลื่อนไหวพร้อมการโทรกลับ เพื่อให้ส่งคืนสัญญาแทนที่จะยอมรับการโทรกลับ
การใช้งานใหม่:
showCircle(150, 150, 100).แล้ว(div => { div.classList.add('ข้อความบอล'); div.append("สวัสดีชาวโลก!"); -
แก้ไขปัญหาของงาน แวดวงเคลื่อนไหว โดยมีการโทรกลับเป็นฐาน
เปิดโซลูชันในแซนด์บ็อกซ์