ไม่ใช่ข้อผิดพลาดทั้งหมดที่เป็นอันตรายถึงชีวิต บางคนแค่ระบุว่าคุณต้องลองอีกครั้ง โชคดีที่ Ruby มีกลไกที่น่าสนใจบางอย่างที่ช่วยให้ "ลองอีกครั้ง" ได้ง่าย แม้ว่าจะไม่ได้ชัดเจนหรือเป็นที่รู้จักทั้งหมดก็ตาม ในโพสต์นี้ เราจะมาดูกลไกเหล่านี้และวิธีการทำงานในโลกของความเป็นจริง
แนะนำ retry
โอเค อันนี้ค่อนข้างชัดเจน แต่ถ้าคุณรู้ว่ามันมีอยู่จริง โดยส่วนตัวแล้ว ฉันคุ้นเคยกับอาชีพ Ruby เป็นอย่างดีก่อนที่ฉันจะได้เรียนรู้เกี่ยวกับคีย์เวิร์ด "ลองใหม่" ที่น่ายินดี
ลองใหม่อีกครั้งถูกสร้างขึ้นในระบบการช่วยเหลือข้อยกเว้นของ Ruby มันค่อนข้างง่าย หากคุณใช้ "ลองใหม่" ในบล็อกการกู้คืน จะทำให้ส่วนโค้ดที่ได้รับการช่วยเหลือกลับมาทำงานอีกครั้ง มาดูตัวอย่างกัน
begin
retries ||= 0
puts "try ##{ retries }"
raise "the roof"
rescue
retry if (retries += 1) < 3
end
# ... outputs the following:
# try #0
# try #1
# try #2
มีบางสิ่งที่ควรทราบที่นี่:
-
เมื่อเรียกลองใหม่ ทั้งหมด ของรหัสระหว่างการเริ่มต้นและการกู้คืนจะถูกเรียกใช้อีกครั้ง แน่นอนที่สุด ใช่ ไม่ "ไปต่อจากที่ค้างไว้" หรืออะไรทำนองนั้น
-
หากคุณไม่มีกลไกบางอย่างในการจำกัดการลองใหม่ คุณจะจบลงด้วยการวนซ้ำที่ไม่สิ้นสุด
-
โค้ดทั้งในบล็อกเริ่มต้นและบล็อกช่วยเหลือสามารถเข้าถึงตัวแปรการลองใหม่เดียวกันในขอบเขตหลักได้
ปัญหาเกี่ยวกับ retry
แม้ว่าการลองใหม่จะดีมาก แต่ก็มีข้อจำกัดบางประการ สิ่งสำคัญคือการที่บล็อกเริ่มต้นทั้งหมดถูกเรียกใช้ซ้ำ แต่บางครั้งก็ไม่เหมาะ
ตัวอย่างเช่น ลองจินตนาการว่าคุณกำลังใช้อัญมณีที่ให้คุณโพสต์การอัปเดตสถานะไปยัง Twitter, Facebook และเว็บไซต์อื่นๆ ได้โดยใช้การเรียกใช้เมธอดเดียว อาจมีลักษณะเช่นนี้
SocialMedia.post_to_all("Zomg! I just ate the biggest hamburger")
# ...posts to Twitter API
# ...posts to Facebook API
# ...etc
หาก API ตัวใดตัวหนึ่งไม่ตอบสนอง เจมจะสร้าง SocialMedia::TimeoutError และยกเลิก หากเราตรวจพบข้อยกเว้นนี้และลองอีกครั้ง เราจะจบลงด้วยการโพสต์ที่ซ้ำกันเนื่องจากการลองใหม่จะเริ่มใหม่ตั้งแต่ต้น
begin
SocialMedia.post_to_all("Zomg! I just ate the biggest hamburger")
rescue SocialMedia::TimeoutError
retry
end
# ...posts to Twitter API
# facebook error
# ...posts to Twitter API
# facebook error
# ...posts to Twitter API
# and so on
คงจะดีไม่น้อยถ้าเราสามารถบอกอัญมณีว่า "เพียงแค่ข้าม facebook และไปที่รายการ API ที่จะโพสต์ต่อไป"
โชคดีสำหรับเรา Ruby ช่วยให้เราทำอย่างนั้นได้
หมายเหตุ:แน่นอนว่าวิธีแก้ปัญหาที่แท้จริงคือการสร้างไลบรารีโซเชียลมีเดียใหม่ แต่นี่ยังห่างไกลจากกรณีการใช้งานเพียงอย่างเดียวสำหรับเทคนิคที่ฉันจะแสดงให้คุณเห็น
ความต่อเนื่องในการช่วยชีวิต
ความต่อเนื่องมักจะทำให้ผู้คนหวาดกลัว แต่นั่นเป็นเพียงเพราะพวกเขาไม่ได้ใช้บ่อยนักและดูแปลกไปเล็กน้อย แต่เมื่อคุณเข้าใจพื้นฐานแล้ว มันก็ค่อนข้างง่าย
ความต่อเนื่องเป็นเหมือน "จุดบันทึก" ในโค้ดของคุณ เช่นเดียวกับในวิดีโอเกม คุณสามารถออกไปทำอย่างอื่นได้ จากนั้นกระโดดกลับไปที่จุดเซฟ แล้วทุกอย่างจะเป็นเหมือนเดิม
...โอเค มันไม่ใช่การเปรียบเทียบที่สมบูรณ์แบบ แต่ใช้งานได้ดี มาดูโค้ดกัน:
require "continuation"
counter = 0
continuation = callcc { |c| c } # define our savepoint
puts(counter += 1)
continuation.call(continuation) if counter < 5 # jump back to our savepoint
คุณอาจสังเกตเห็นสิ่งแปลก ๆ บางอย่าง ผ่านมันไปกันเถอะ:
-
เราใช้เมธอด callcc เพื่อสร้างออบเจ็กต์ Continuation ไม่มีไวยากรณ์ OO ที่สะอาดสำหรับสิ่งนี้
-
ครั้งแรกที่
continuation
กำหนดตัวแปรไว้ โดยกำหนดให้เป็นค่าส่งคืนของcallcc
บล๊อก. นั่นเป็นเหตุผลที่ต้องมีบล็อกอยู่ที่นั่น -
ทุกครั้งที่เรากระโดดกลับไปที่จุดบันทึก
continuation
ตัวแปรถูกกำหนดอาร์กิวเมนต์ที่เราส่งcall
กระบวนการ. นั่นเป็นเหตุผลที่เราใช้continuation.call(continuation)
. ที่ดูแปลกตา ไวยากรณ์
การเพิ่มความต่อเนื่องให้กับข้อยกเว้น
เราจะใช้ความต่อเนื่องเพื่อเพิ่ม skip
วิธีการยกเว้นทั้งหมด ตัวอย่างด้านล่างแสดงให้เห็นว่าควรทำงานอย่างไร เมื่อใดก็ตามที่ฉันช่วยเหลือข้อยกเว้น ฉันควรจะสามารถเรียก skip
ซึ่งจะทำให้โค้ดที่ยกข้อยกเว้นทำเหมือนไม่เคยเกิดขึ้น
begin
raise "the roof"
puts "The exception was ignored"
rescue => e
e.skip
end
# ...outputs "The exception was ignored"
การทำเช่นนี้ฉันจะต้องทำบาปเล็กน้อย Exception
เป็นเพียงชั้นเรียน นั่นหมายความว่าฉันสามารถ monekypatch เพื่อเพิ่ม skip
วิธีการ
class Exception
attr_accessor :continuation
def skip
continuation.call
end
end
ตอนนี้เราต้องตั้งค่า continuation
คุณลักษณะสำหรับทุกข้อยกเว้น ปรากฎว่า raise
เป็นเพียงวิธีการซึ่งเราสามารถแทนที่ได้
BTW รหัสด้านล่างใช้เกือบทุกคำจากสไลด์ที่ยอดเยี่ยมของ Advi สิ่งที่คุณไม่รู้เกี่ยวกับข้อยกเว้น ฉันไม่สามารถคิดหาวิธีที่ดีไปกว่านี้:
require 'continuation'
module StoreContinuationOnRaise
def raise(*args)
callcc do |continuation|
begin
super
rescue Exception => e
e.continuation = continuation
super(e)
end
end
end
end
class Object
include StoreContinuationOnRaise
end
ตอนนี้ฉันสามารถเรียก skip
วิธีการสำหรับข้อยกเว้นใด ๆ และมันจะเป็นเหมือนข้อยกเว้นที่ไม่เคยเกิดขึ้น