คำนำ
วัตถุสัญญาสามารถเข้าใจได้ว่าเป็นการดำเนินการที่จะดำเนินการ (มักใช้สำหรับการดำเนินการแบบอะซิงโครนัส) หลังจากใช้วัตถุสัญญาแล้วรหัสสามารถจัดระเบียบในวิธีการโทรแบบโซ่เพื่อให้รหัสใช้งานง่ายขึ้น ยิ่งกว่านั้นเนื่องจากการมีอยู่ของวิธีการเช่นสัญญาทั้งหมดมันสามารถทำได้ง่ายในการดำเนินการหลายอย่างในเวลาเดียวกัน
การเพิ่มขึ้นของสัญญาเป็นเพราะการโทรแบบอะซิงโครนัสฟังก์ชันการโทรกลับมักเกิดขึ้นหลังจากนั้นอีก สถานการณ์นี้นำไปสู่การเกิดขึ้นของปัญหาพีระมิดโทรกลับ ไม่เพียง แต่รหัสยากและไม่สวยงามในการเขียน แต่ยังทำให้ยากสำหรับผู้ที่อ่านรหัสเพื่อทำความเข้าใจเมื่อปัญหามีความซับซ้อน
เป็นตัวอย่าง:
db.save (data, function (data) {// ทำอะไรบางอย่าง ... db.save (data1, function (data) {// ทำอะไรบางอย่าง ... db.save (data2, ฟังก์ชั่น (data) {// ทำอะไรบางอย่าง ... เสร็จแล้ว (data3); // ส่งคืนข้อมูล})});}); สมมติว่ามีการดำเนินการบันทึกฐานข้อมูลและคำขอต้องการข้อมูลที่จะบันทึกสามครั้งในสามตาราง จากนั้นรหัสของเราจะคล้ายกับรหัสข้างต้น ฉันควรทำอย่างไรถ้ามีปัญหาใน db.save ที่สองในเวลานี้? จากการพิจารณานี้เราจำเป็นต้องใช้ตรรกะเช่น try...catch ในแต่ละเลเยอร์ของการโทรกลับ นี่คือแหล่งที่มาของความชั่วร้ายทั้งหมดและเป็นจุดที่โหนดถูกวิพากษ์วิจารณ์อย่างกว้างขวางในตอนแรก
ข้อเสียอีกประการหนึ่งคือสมมติว่าไม่มีการพึ่งพาด้านหน้าและหลังระหว่างสามบันทึกของเราเรายังคงต้องรอฟังก์ชันก่อนหน้านี้จะถูกดำเนินการก่อนที่จะดำเนินการขั้นตอนต่อไปและการบันทึกทั้งสามไม่สามารถขนานได้ (หรือต้องการทักษะในการใช้งาน)
น่าเสียดายที่เมื่อฉันเริ่มมีส่วนร่วมในโหนดฉันเขียนนรกมากมายเช่นนี้
ต่อมาเพราะฉันยังคงเขียนรหัสส่วนหน้าเพิ่มเติมฉันจึงติดต่อกับ ES6 และพบเครื่องมือที่ทรงพลังสำหรับการแก้ปัญหาการโทรกลับ
ในความเป็นจริงนานก่อนที่ ES6 Promise, Q, When.js, Bluebird และห้องสมุดอื่น ๆ ได้สร้างล้อสัญญาของตัวเองตามมาตรฐานสัญญา (อ้างถึง Promise/A+)
(ฉันอ่านบทความและฉันคิดว่ามันสมเหตุสมผลแล้วมันบอกว่าคุณไม่ควรขยายวัตถุดั้งเดิมในตัววิธีการนี้ไม่สามารถมุ่งเน้นในอนาคตได้ดังนั้นนี่คือเคล็ดลับ: ระมัดระวังเมื่อใช้ห้องสมุดที่ขยายสัญญาดั้งเดิม)
มีการกล่าวถึงคำสัญญาดั้งเดิมเท่านั้นที่นี่
ES6 Promise
สัญญาสถานะวัตถุ
ก่อนที่จะอธิบายคำสัญญาโดยละเอียดก่อนอื่นให้ใช้ทฤษฎี:
Promise/A+ Specification กำหนดว่าวัตถุสัญญาเป็นเครื่องสถานะ จำกัด
มีสามรัฐ:
1. pending (ดำเนินการ)
2. fulfilled (ประสบความสำเร็จ)
3. reject
ในกรณี pending คือสถานะเริ่มต้น fulfilled และ rejected คือสถานะสิ้นสุด (สถานะสุดท้ายบ่งชี้ว่าวงจรชีวิตของสัญญาสิ้นสุดลง)
ความสัมพันธ์การเปลี่ยนแปลงของรัฐคือ:
รอดำเนินการ-> เติมเต็มรอ-> ถูกปฏิเสธ
เหตุการณ์ต่าง ๆ (เช่นเหตุการณ์การดำเนินการที่ประสบความสำเร็จเหตุการณ์การดำเนินการที่ล้มเหลว ฯลฯ ) จะถูกเรียกใช้เป็นช่วงการเปลี่ยนภาพของรัฐ
แบบฟอร์มสัญญา
สัญญามีลักษณะเช่นนี้:
var promise = new สัญญา (ฟังก์ชั่น func (แก้ไข, ปฏิเสธ) {// ทำบางสิ่งบางอย่างบางที async ถ้า (ความสำเร็จ) {return resolve (data);} else {return reject (data);}}); Promise. แล้วฟังก์ชั่น (data) {// ทำอะไรบางอย่าง ... เช่น console.log (data);สัญญาตัวแปรที่นี่เป็นตัวอย่างของวัตถุสัญญา
เมื่อวัตถุสัญญาถูกสร้างขึ้นลอจิกในฟังก์ชัน func จะถูกดำเนินการ
เมื่อตรรกะถูกประมวลผลและไม่มีข้อผิดพลาด resolve การเรียกกลับจะส่งผ่านค่าไปยังสถานที่พิเศษ สถานที่พิเศษนี้อยู่ที่ไหน นั่นคือในรหัสต่อไปนี้ เราใช้ฟังก์ชั่นการโทรกลับใน then เพื่อประมวลผลผลลัพธ์หลังจาก resolve ตัวอย่างเช่นในรหัสข้างต้นเราเพียงแค่ส่งออกค่าไปยังคอนโซล หากมีข้อผิดพลาด reject ฟังก์ชันการโทรกลับที่สองของ then เพื่อประมวลผลข้อผิดพลาด
สอดคล้องกับทฤษฎีข้างต้นของเครื่อง จำกัด สถานะเรารู้ว่าเมื่อดำเนินการรหัสฟังก์ชั่นการเรียกกลับในตัวสร้างสัญญารัฐกำลัง pending การสถานะจะ fulfilled หลังจาก resolve และรัฐจะถูก reject หลังจาก reject
สัญญาการไหลของข้อมูล
ข้างต้นคือการไหลของข้อมูลครั้งแรก
สิ่งที่ตลกคือวิธีการตามสัญญา then ยังสามารถส่งคืนวัตถุสัญญาเพื่อให้เราสามารถใช้สิ่งต่อไป then เพื่อทำการประมวลผลเดียวกัน
ฟังก์ชั่นการโทรกลับสองฟังก์ชั่นในครั้งแรก then พิจารณาว่าวัตถุสัญญาประเภทใดที่ส่งคืน then แรก
สมมติว่าการโทรกลับครั้งแรกของครั้งแรก then ไม่ได้ส่งคืนวัตถุสัญญาจากนั้นผู้โทรของคนที่สอง then ก็ยังคงเป็นวัตถุสัญญาดั้งเดิมยกเว้นว่าค่าการแก้ไขจะกลายเป็นค่าคืนของฟังก์ชันการเรียกกลับครั้งแรกในตอนแรก
สมมติว่าฟังก์ชั่นการโทรกลับครั้งแรก then ส่งคืนวัตถุสัญญาครั้งที่สอง then ผู้โทรจะกลายเป็นวัตถุสัญญาใหม่นี้และที่สอง then รอให้วัตถุสัญญาใหม่แก้ไขหรือปฏิเสธและเรียกใช้การโทรกลับ
แม้ว่าฉันจะรอดพ้นเล็กน้อย แต่ฉันรู้สึกว่าฉันยังคงชัดเจนเกี่ยวกับเรื่องนี้ ฮ่าฮ่า ~
หากพบข้อผิดพลาดที่ใดก็ได้ข้อผิดพลาดจะถูกส่งไปยังฟังก์ชั่นการโทรกลับที่สอง then กับฟังก์ชั่นการโทรกลับที่สองเพื่อจัดการ สามารถเข้าใจได้ว่าข้อผิดพลาด reject ไปข้างหลังจนกว่าจะดำเนินการ
นอกจากนี้วัตถุสัญญายังมีวิธีการ catch ซึ่งยอมรับฟังก์ชั่นการโทรกลับเพื่อจัดการกับข้อผิดพลาด
ตอนนี้:
Promise.catch (ฟังก์ชั่น (err) {// จัดการ err.})สมมติว่าการจัดการข้อผิดพลาดมีความคล้ายคลึงกันวิธีนี้สามารถจัดการกับข้อผิดพลาดส่วนกลางและสม่ำเสมอ ดังนั้นวิธีอื่น ๆ ไม่จำเป็นต้องโทรกลับครั้งที่สอง ~
ควบคุมสัญญาพร้อมกัน
สัญญามี "วิธีการคงที่" - Promise.all (โปรดทราบว่าไม่ใช่ Promise.prototype) วิธีนี้ยอมรับองค์ประกอบที่เป็นอาร์เรย์ของวัตถุสัญญา
วิธีนี้ยังส่งคืนวัตถุสัญญา หากวัตถุสัญญาทั้งหมดในอาร์เรย์ resolve แล้วค่า resolve จะถูกใช้เป็นอาร์เรย์เป็นค่า resolve ของ (คำสัญญา) ของค่าส่งคืนของวิธี Promise.all ทั้งหมดและจากนั้นสามารถประมวลผลได้โดยวิธี then หากคำสัญญาใด ๆ ในอาร์เรย์ถูก reject ค่า reject คือค่า reject ของค่าผลตอบแทนของวิธี Promise.all ทั้งหมด
จุดทึบแสงมากคือ:
ลำดับของค่า resolve (ดังที่ได้กล่าวไว้ข้างต้นอาร์เรย์) ที่ได้รับจากฟังก์ชั่นการโทรกลับครั้งแรกของวิธี then เหมือนกับลำดับของอาร์เรย์พารามิเตอร์ใน Promise.all แทนที่จะเป็นลำดับตามลำดับเวลา
นอกจากนี้ยังมีวิธีการที่คล้ายกับ Promise.all Promise.race ซึ่งได้รับอาร์เรย์ยกเว้นว่าจะยอมรับค่าแรก resolve เท่านั้น
เปลี่ยนวัตถุอื่น ๆ ให้เป็นวัตถุสัญญา
วิธี Promise.resovle สามารถส่งคืนวัตถุสัญญาเป็นพารามิเตอร์
มีสองสถานการณ์:
สมมติว่าไม่มีวิธี .then ของพารามิเตอร์ที่ผ่านจากนั้นวัตถุสัญญาที่ส่งคืนจะกลายเป็นสถานะ resolve และค่า resolve คือวัตถุเอง
สมมติว่าพารามิเตอร์ที่ส่งผ่านมีวิธีการ then (เรียกว่าวัตถุ thenable ) จากนั้นประเภทของวัตถุนี้จะเปลี่ยนเป็นสัญญาและวิธีการนั้นจะกลายเป็น Promise.prototype.then then วิธีการ
สัญญาว่าเป็นวิธีแก้ปัญหาแบบอะซิงโครนัสหรือไม่?
ในที่สุดขอให้ฉันพูดสิ่งที่สำคัญมาก: ฟังก์ชั่นของสัญญาคือการแก้ปัญหาการโทรกลับปิรามิดและมันก็ไม่ได้มีบทบาทสำคัญในการควบคุมกระบวนการแบบอะซิงโครนัส ในการใช้สัญญาอย่างแท้จริงเพื่อควบคุมกระบวนการแบบอะซิงโครนัสเรายังต้องใช้ฟังก์ชั่น ES6 generator (ตัวอย่างเช่นการใช้งาน CO Library ของ TJ Master)
อย่างไรก็ตาม ES7 จะมีทางออกที่ยอดเยี่ยมกว่า: async/await ซึ่งคล้ายกับ CO แต่มีการสนับสนุนแบบดั้งเดิม รอดู
สรุป
ข้างต้นเป็นเรื่องเกี่ยวกับสัญญาดั้งเดิมใน JavaScript ES6 ฉันหวังว่าเนื้อหาของบทความนี้จะเป็นประโยชน์กับทุกคนในการเรียนรู้ ES6 หากคุณมีคำถามใด ๆ โปรดฝากข้อความเพื่อสื่อสาร