การจัดการข้อยกเว้นที่ดีที่สุดสำหรับ Node.js


755

ฉันเพิ่งเริ่มลอง node.js ไม่กี่วันที่ผ่านมา ฉันรู้ว่าโหนดถูกยกเลิกเมื่อใดก็ตามที่ฉันมีข้อยกเว้นที่ไม่สามารถจัดการได้ในโปรแกรมของฉัน สิ่งนี้แตกต่างจากคอนเทนเนอร์เซิร์ฟเวอร์ปกติที่ฉันเคยเห็นที่ Worker Thread เท่านั้นที่ตายเมื่อเกิดข้อยกเว้นที่ไม่สามารถจัดการได้และคอนเทนเนอร์จะยังคงสามารถรับคำขอได้ นี่ทำให้เกิดคำถามสองสามข้อ:

  • เป็นprocess.on('uncaughtException')วิธีเดียวที่มีประสิทธิภาพในการป้องกันหรือไม่
  • จะprocess.on('uncaughtException')จับข้อยกเว้นที่ไม่สามารถจัดการได้ในระหว่างการดำเนินการของกระบวนการอะซิงโครนัสด้วยหรือไม่
  • มีโมดูลที่สร้างขึ้นแล้ว (เช่นส่งอีเมลหรือเขียนไปยังไฟล์) ที่ฉันสามารถใช้ประโยชน์ในกรณีที่มีข้อยกเว้นที่ไม่ได้ตรวจสอบหรือไม่

ฉันขอขอบคุณตัวชี้ / บทความใด ๆ ที่จะแสดงแนวทางปฏิบัติที่ดีที่สุดสำหรับการจัดการข้อยกเว้นที่ไม่ได้ตรวจสอบใน node.js


11
ข้อยกเว้นที่ไม่ถูกตรวจจับไม่ควรเกิดขึ้น หากพวกเขาใช้โปรแกรมที่รีสตาร์ทแอปพลิเคชันทั้งหมดของคุณเมื่อระบบล่ม (nodemon, ตลอดไป, หัวหน้างาน)
Raynos

116
ข้อยกเว้นที่ไม่ถูกตรวจสอบสามารถเกิดขึ้นได้เสมอหากคุณไม่ใส่รหัสอะซิงโครนัสทุกชิ้นในtry .. catchนั้นและตรวจสอบสิ่งนี้ได้เช่นกันสำหรับlibs ทั้งหมดของคุณ
Dan

13
+1 ด่านในตอนแรกฉันคิดว่าlibs ทั้งหมดของคุณพูดเกินจริงเล็กน้อยเนื่องจากคุณ "เพียง แต่" จำเป็นต้องรวม "จุดเข้าเธรด" ของคุณทั้งหมดไว้ในรหัสเมื่อลอง / จับ แต่เมื่อคิดถึงเรื่องนี้อย่างละเอียดยิ่งขึ้น lib ใด ๆ ที่มีsetTimeoutหรือsetIntervalฝังอยู่ในที่ใดที่หนึ่งซึ่งโค้ดของคุณไม่สามารถจับได้
Eugene Beresovsky

8
@EugeneBeresovksy Dan นั้นถูกต้อง แต่ก็ไม่ได้เปลี่ยนความจริงที่ว่าเมื่อ uncaughtExceptions เกิดขึ้นตัวเลือกที่ปลอดภัยเพียงอย่างเดียวคือการรีสตาร์ทแอป กล่าวอีกนัยหนึ่งแอปของคุณขัดข้องและไม่มีอะไรที่คุณสามารถทำได้หรือควรทำ หากคุณต้องการทำสิ่งที่สร้างสรรค์ใช้คุณสมบัติโดเมนใหม่และยังคงอยู่ในช่วงทดลอง v0.8 เพื่อให้คุณสามารถบันทึกความผิดพลาดและส่งการตอบสนอง 5xx ไปยังไคลเอนต์ของคุณ
ostergaard

1
@Dan แม้จะมีฟังก์ชั่นการโทรกลับทั้งหมดในความพยายาม .. catch ไม่รับประกันว่าจะได้รับข้อผิดพลาด ในกรณีที่จำเป็นต้องใช้โมดูลมันเป็นไบนารีของตัวเองพวกเขาสามารถผิดพลาดอย่างไม่มีมารยาท ฉันเคยมีสิ่งนี้เกิดขึ้นกับ phantomjs-node ล้มเหลวจากข้อผิดพลาดที่เป็นไปไม่ได้ที่จะจับได้ (เว้นแต่ฉันจะทำการตรวจสอบกระบวนการบางอย่างบนไบนารีที่ต้องการ แต่ฉันไม่เคยทำอย่างนั้น)
Trindaz

คำตอบ:


737

อัปเดต: ตอนนี้ Joyent มีไกด์ของตัวเองแล้ว ข้อมูลต่อไปนี้เป็นข้อสรุปที่มากกว่า:

ข้อผิดพลาด "การขว้างปา" อย่างปลอดภัย

โดยหลักการแล้วเราต้องการหลีกเลี่ยงข้อผิดพลาดที่ไม่ถูกตรวจจับได้มากที่สุดเช่นแทนที่จะทิ้งความผิดพลาดอย่างแท้จริงเราสามารถ "โยน" ข้อผิดพลาดได้อย่างปลอดภัยโดยใช้วิธีใดวิธีหนึ่งต่อไปนี้ขึ้นอยู่กับสถาปัตยกรรมรหัสของเรา

  • สำหรับรหัสซิงโครนัสหากเกิดข้อผิดพลาดให้ส่งคืนข้อผิดพลาด:

    // Define divider as a syncrhonous function
    var divideSync = function(x,y) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by returning it
            return new Error("Can't divide by zero")
        }
        else {
            // no error occured, continue on
            return x/y
        }
    }
    
    // Divide 4/2
    var result = divideSync(4,2)
    // did an error occur?
    if ( result instanceof Error ) {
        // handle the error safely
        console.log('4/2=err', result)
    }
    else {
        // no error occured, continue on
        console.log('4/2='+result)
    }
    
    // Divide 4/0
    result = divideSync(4,0)
    // did an error occur?
    if ( result instanceof Error ) {
        // handle the error safely
        console.log('4/0=err', result)
    }
    else {
        // no error occured, continue on
        console.log('4/0='+result)
    }
  • สำหรับ (ie. ไม่ตรงกัน) รหัสโทรกลับตามอาร์กิวเมนต์แรกของการเรียกกลับคือerrถ้ามีข้อผิดพลาดเกิดขึ้นerrเป็นข้อผิดพลาดหากมีข้อผิดพลาดไม่ได้เกิดขึ้นแล้วคือerr nullอาร์กิวเมนต์อื่นใดก็ตามที่เป็นไปตามerrอาร์กิวเมนต์:

    var divide = function(x,y,next) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by calling the completion callback
            // with the first argument being the error
            next(new Error("Can't divide by zero"))
        }
        else {
            // no error occured, continue on
            next(null, x/y)
        }
    }
    
    divide(4,2,function(err,result){
        // did an error occur?
        if ( err ) {
            // handle the error safely
            console.log('4/2=err', err)
        }
        else {
            // no error occured, continue on
            console.log('4/2='+result)
        }
    })
    
    divide(4,0,function(err,result){
        // did an error occur?
        if ( err ) {
            // handle the error safely
            console.log('4/0=err', err)
        }
        else {
            // no error occured, continue on
            console.log('4/0='+result)
        }
    })
  • สำหรับรหัสที่สำคัญซึ่งข้อผิดพลาดอาจเกิดขึ้นได้ทุกที่แทนที่จะโยนข้อผิดพลาดให้ดำเนินการerrorเหตุการณ์แทน :

    // Definite our Divider Event Emitter
    var events = require('events')
    var Divider = function(){
        events.EventEmitter.call(this)
    }
    require('util').inherits(Divider, events.EventEmitter)
    
    // Add the divide function
    Divider.prototype.divide = function(x,y){
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by emitting it
            var err = new Error("Can't divide by zero")
            this.emit('error', err)
        }
        else {
            // no error occured, continue on
            this.emit('divided', x, y, x/y)
        }
    
        // Chain
        return this;
    }
    
    // Create our divider and listen for errors
    var divider = new Divider()
    divider.on('error', function(err){
        // handle the error safely
        console.log(err)
    })
    divider.on('divided', function(x,y,result){
        console.log(x+'/'+y+'='+result)
    })
    
    // Divide
    divider.divide(4,2).divide(4,0)

ข้อผิดพลาด "การจับ" อย่างปลอดภัย

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

  • เมื่อเราทราบว่ามีข้อผิดพลาดเกิดขึ้นเราสามารถตัดส่วนนั้นในโดเมน node.js

    var d = require('domain').create()
    d.on('error', function(err){
        // handle the error safely
        console.log(err)
    })
    
    // catch the uncaught errors in this asynchronous or synchronous code block
    d.run(function(){
        // the asynchronous or synchronous code that we want to catch thrown errors on
        var err = new Error('example')
        throw err
    })
  • หากเราทราบว่าข้อผิดพลาดเกิดขึ้นคือรหัสซิงโครนัสและด้วยเหตุผลใดก็ตามที่ไม่สามารถใช้โดเมน (อาจเป็นโหนดรุ่นเก่า) เราสามารถใช้คำสั่ง try catch:

    // catch the uncaught errors in this synchronous code block
    // try catch statements only work on synchronous code
    try {
        // the synchronous code that we want to catch thrown errors on
        var err = new Error('example')
        throw err
    } catch (err) {
        // handle the error safely
        console.log(err)
    }

    อย่างไรก็ตามระวังไม่ให้ใช้try...catchในโค้ดแบบอะซิงโครนัสเนื่องจากข้อผิดพลาดที่เกิดจากการโยนแบบอะซิงโครนัสจะไม่ถูกตรวจจับ:

    try {
        setTimeout(function(){
            var err = new Error('example')
            throw err
        }, 1000)
    }
    catch (err) {
        // Example error won't be caught here... crashing our app
        // hence the need for domains
    }

    หากคุณต้องการทำงานtry..catchร่วมกับรหัสอะซิงโครนัสเมื่อใช้ Node 7.4 หรือสูงกว่าคุณสามารถใช้async/awaitnatively ในการเขียนฟังก์ชันอะซิงโครนัสของคุณ

    สิ่งที่ต้องระวังอีกประการหนึ่งtry...catchคือความเสี่ยงที่จะทำให้การโทรกลับเสร็จสิ้นภายในtryข้อความสั่งดังนี้:

    var divide = function(x,y,next) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by calling the completion callback
            // with the first argument being the error
            next(new Error("Can't divide by zero"))
        }
        else {
            // no error occured, continue on
            next(null, x/y)
        }
    }
    
    var continueElsewhere = function(err, result){
            throw new Error('elsewhere has failed')
    }
    
    try {
            divide(4, 2, continueElsewhere)
            // ^ the execution of divide, and the execution of 
            //   continueElsewhere will be inside the try statement
    }
    catch (err) {
            console.log(err.stack)
            // ^ will output the "unexpected" result of: elsewhere has failed
    }

    gotcha นี้ทำง่ายมากเนื่องจากรหัสของคุณซับซ้อนมากขึ้น ดังนั้นจึงเป็นการดีที่สุดที่จะใช้โดเมนหรือส่งคืนข้อผิดพลาดเพื่อหลีกเลี่ยง (1) ข้อยกเว้นที่ไม่ถูกตรวจจับในรหัสอะซิงโครนัส (2) การลองจับการกระทำที่คุณไม่ต้องการ ในภาษาที่อนุญาตให้เธรดที่เหมาะสมแทนสไตล์เหตุการณ์เครื่องแบบอะซิงโครนัสของ JavaScript นี่เป็นปัญหาน้อยกว่า

  • สุดท้ายในกรณีที่มีข้อผิดพลาดเกิดขึ้น uncaught ในสถานที่ที่ไม่ได้ห่อในโดเมนหรือคำสั่งที่ลองจับเราสามารถทำให้โปรแกรมของเราไม่ผิดพลาดโดยใช้uncaughtExceptionฟัง ( แต่การทำเช่นนี้สามารถนำประยุกต์ใช้ในรัฐที่ไม่รู้จัก ):

    // catch the uncaught errors that weren't wrapped in a domain or try catch statement
    // do not use this in modules, but only in applications, as otherwise we could have multiple of these bound
    process.on('uncaughtException', function(err) {
        // handle the error safely
        console.log(err)
    })
    
    // the asynchronous or synchronous code that emits the otherwise uncaught error
    var err = new Error('example')
    throw err

5
ขอบคุณ Raynos อัปเดตแล้ว คุณมีแหล่งที่อธิบายถึงความชั่วร้ายtry catchหรือไม่? อย่างที่ฉันชอบที่จะสำรองหลักฐานไว้ แก้ไขตัวอย่างการซิงค์ด้วย
balupton

2
คำตอบนี้ไม่ถูกต้องอีกต่อไป โดเมนแก้ปัญหานี้ (แนะนำโดย node.js)
Gabriel Llamas

5
@balupton ข้อผิดพลาดควรถูกส่งไปเพื่อจัดการข้อผิดพลาด พวกเขาไม่ควรหลีกเลี่ยงอย่างแน่นอน ไม่มีอะไรเกี่ยวกับพวกเขาที่หยุดการทำงานของแอพหรือสิ่งอื่นใด Java และภาษาสมัยใหม่อื่น ๆ ส่วนใหญ่มีการสนับสนุนที่ยอดเยี่ยมสำหรับข้อยกเว้น ข้อสรุปเดียวของฉันหลังจากอ่านโพสต์ misonformed บางส่วนที่นี่คือผู้คนไม่เข้าใจพวกเขาดีมากและกลัวพวกเขา กลัวความสงสัยที่ไม่แน่นอน การถกเถียงครั้งนี้ได้ตัดสินใจสรุปไว้อย่างดีว่าด้วยข้อยกเว้นอย่างน้อย 20 ปีก่อน
enl8enmentnow

22
ขณะนี้โดเมนถูกเลิกใช้โดย io.js : " โมดูลนี้อยู่ระหว่างการคัดค้านเมื่อ API การแทนที่ได้รับการสรุปโมดูลนี้จะถูกคัดค้านอย่างสมบูรณ์ ... ผู้ใช้ที่ต้องมีฟังก์ชั่นการใช้งานที่โดเมนจัดเตรียมไว้อาจใช้เวลา ควรคาดหวังว่าจะต้องย้ายไปยังโซลูชันอื่นในอนาคต "
Timothy Gu

5
API โดเมนจะเลิกตอนนี้ ? พวกเขาพูดถึง API แทน - ทุกคนรู้ว่าเมื่อไหร่จะเกิดขึ้นและสิ่งที่มันจะมีลักษณะอย่างไร
UpTheCreek

95

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


แนวทางปฏิบัติที่ดีที่สุดของการจัดการข้อผิดพลาด Node.JS


หมายเลข 1: ใช้สัญญาสำหรับการจัดการข้อผิดพลาด async

TL; DR: การจัดการข้อผิดพลาด async ในรูปแบบการโทรกลับอาจเป็นวิธีที่เร็วที่สุดในการตกนรก (หรือที่รู้จักกันในชื่อปิรามิดแห่งการลงโทษ) ของขวัญที่ดีที่สุดที่คุณสามารถมอบให้กับรหัสของคุณใช้แทนไลบรารี่สัญญาที่มีชื่อเสียงซึ่งให้รูปแบบโค้ดที่กะทัดรัดและคุ้นเคยเช่นลองจับ

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

ตัวอย่างรหัส - ดี

doWork()
.then(doWork)
.then(doError)
.then(doWork)
.catch(errorHandler)
.then(verify);

โค้ดตัวอย่างรูปแบบการต่อต้าน - การจัดการข้อผิดพลาดสไตล์การติดต่อกลับ

getData(someParameter, function(err, result){
    if(err != null)
      //do something like calling the given callback function and pass the error
    getMoreData(a, function(err, result){
          if(err != null)
            //do something like calling the given callback function and pass the error
        getMoreData(b, function(c){ 
                getMoreData(d, function(e){ 
                    ...
                });
            });
        });
    });
});

คำพูดบล็อก: "เรามีปัญหากับคำสัญญา" (จาก pouchdb บล็อกอันดับ 11 สำหรับคำหลัก "สัญญาโหนด")

"... และในความเป็นจริงการเรียกกลับทำอะไรที่น่ากลัวกว่าเดิม: พวกมันกีดกันพวกเราในกองซ้อนซึ่งเป็นสิ่งที่เรามักจะให้สิทธิ์ในการเขียนโปรแกรมภาษาการเขียนโค้ดโดยไม่มีกองซ้อนนั้นเหมือนกับการขับรถโดยไม่มีแป้นเหยียบ: ไม่ทราบว่าคุณต้องการมันมากแค่ไหนจนกระทั่งมันมาถึงและมันไม่มีอยู่จุดรวมของสัญญาคือให้พื้นฐานภาษาที่เราสูญเสียไปเมื่อเราไป async: return, throw, and stack แต่คุณ ต้องรู้วิธีใช้สัญญาอย่างถูกต้องเพื่อใช้ประโยชน์จากพวกเขา "


หมายเลข 2: ใช้เฉพาะวัตถุข้อผิดพลาดในตัว

TL; DR:เป็นเรื่องปกติที่จะเห็นรหัสที่ทำให้เกิดข้อผิดพลาดเป็นสตริงหรือเป็นประเภทที่กำหนดเองซึ่งจะทำให้ตรรกะการจัดการข้อผิดพลาดและความสามารถในการทำงานร่วมกันระหว่างโมดูลนั้นซับซ้อน ไม่ว่าคุณจะปฏิเสธสัญญา, โยนข้อยกเว้นหรือปล่อยข้อผิดพลาด - การใช้ Node.JS วัตถุในตัวข้อผิดพลาดเพิ่มความสม่ำเสมอและป้องกันการสูญเสียข้อมูลผิดพลาด

ไม่เช่นนั้น:เมื่อเรียกใช้งานโมดูลบางตัวไม่แน่ใจว่าข้อผิดพลาดประเภทใดที่กลับมา - ทำให้ยากขึ้นมากที่จะให้เหตุผลเกี่ยวกับข้อยกเว้นที่จะเกิดขึ้นและจัดการมัน แม้จะคุ้มค่าการใช้ชนิดกำหนดเองเพื่ออธิบายข้อผิดพลาดอาจนำไปสู่การสูญเสียข้อมูลข้อผิดพลาดที่สำคัญเช่นการติดตามสแต็ก!

ตัวอย่างรหัส - ทำถูกต้อง

    //throwing an Error from typical function, whether sync or async
 if(!productToAdd)
 throw new Error("How can I add new product when no value provided?");

//'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));

//'throwing' an Error from a Promise
 return new promise(function (resolve, reject) {
 DAL.getProduct(productToAdd.id).then((existingProduct) =>{
 if(existingProduct != null)
 return reject(new Error("Why fooling us and trying to add an existing product?"));

ตัวอย่างรหัสรูปแบบการต่อต้าน

//throwing a String lacks any stack trace information and other important properties
if(!productToAdd)
    throw ("How can I add new product when no value provided?");

คำพูดบล็อก: "สตริงไม่ใช่ข้อผิดพลาด" (จากบล็อกความคิดอันดับที่ 6 สำหรับคำค้นหา“ Node.JS error object”)

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


หมายเลข 3: แยกแยะข้อผิดพลาดในการทำงานกับโปรแกรมเมอร์

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

มิฉะนั้นคุณอาจเริ่มต้นแอปพลิเคชันใหม่ตลอดเวลาเมื่อมีข้อผิดพลาดปรากฏขึ้น แต่ทำไมผู้ใช้ออนไลน์ ~ 5,000 รายถึงล้มเหลวเนื่องจากข้อผิดพลาดเล็กน้อยและที่คาดการณ์ไว้ ในทางกลับกันก็ไม่เหมาะเช่นกันการรักษาแอปพลิเคชันไว้เมื่อเกิดปัญหาที่ไม่รู้จัก (ข้อผิดพลาดของโปรแกรมเมอร์) อาจทำให้เกิดพฤติกรรมที่ไม่คาดคิด การแยกความแตกต่างระหว่างสองอย่างนี้ช่วยให้แสดงอย่างมีไหวพริบและใช้วิธีการที่สมดุลตามบริบทที่กำหนด

ตัวอย่างรหัส - ทำถูกต้อง

    //throwing an Error from typical function, whether sync or async
 if(!productToAdd)
 throw new Error("How can I add new product when no value provided?");

//'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));

//'throwing' an Error from a Promise
 return new promise(function (resolve, reject) {
 DAL.getProduct(productToAdd.id).then((existingProduct) =>{
 if(existingProduct != null)
 return reject(new Error("Why fooling us and trying to add an existing product?"));

ตัวอย่างรหัส - การทำเครื่องหมายข้อผิดพลาดเป็นการทำงาน (เชื่อถือได้)

//marking an error object as operational 
var myError = new Error("How can I add new product when no value provided?");
myError.isOperational = true;

//or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object")
function appError(commonType, description, isOperational) {
    Error.call(this);
    Error.captureStackTrace(this);
    this.commonType = commonType;
    this.description = description;
    this.isOperational = isOperational;
};

throw new appError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true);

//error handling code within middleware
process.on('uncaughtException', function(error) {
    if(!error.isOperational)
        process.exit(1);
});

อ้างอิงบล็อก : "มิฉะนั้นคุณเสี่ยงรัฐ" (จากบล็อก debugable อันดับ 3 สำหรับคำหลัก "Node.JS ข้อยกเว้น uncaught")

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


Number4: จัดการข้อผิดพลาดจากส่วนกลางผ่าน แต่ไม่อยู่ในมิดเดิลแวร์

TL; DR:ข้อผิดพลาดในการจัดการตรรกะเช่นเมลไปยังผู้ดูแลระบบและการบันทึกควรถูกห่อหุ้มในวัตถุเฉพาะและเป็นศูนย์กลางที่จุดสิ้นสุดทั้งหมด (เช่นมิดเดิลแวร์ด่วนงาน cron การทดสอบหน่วย) เมื่อเกิดข้อผิดพลาด

มิฉะนั้น: การไม่จัดการข้อผิดพลาดภายในที่เดียวจะนำไปสู่การทำสำเนารหัสและอาจเกิดข้อผิดพลาดที่จัดการอย่างไม่เหมาะสม

ตัวอย่างโค้ด - โฟลว์ข้อผิดพลาดทั่วไป

//DAL layer, we don't handle errors here
DB.addDocument(newCustomer, (error, result) => {
    if (error)
        throw new Error("Great error explanation comes here", other useful parameters)
});

//API route code, we catch both sync and async errors and forward to the middleware
try {
    customerService.addNew(req.body).then(function (result) {
        res.status(200).json(result);
    }).catch((error) => {
        next(error)
    });
}
catch (error) {
    next(error);
}

//Error handling middleware, we delegate the handling to the centrzlied error handler
app.use(function (err, req, res, next) {
    errorHandler.handleError(err).then((isOperationalError) => {
        if (!isOperationalError)
            next(err);
    });
});

คำพูดบล็อก: "บางครั้งระดับที่ต่ำกว่าไม่สามารถทำอะไรได้เลยนอกจากเผยแพร่ข้อผิดพลาดไปยังผู้โทร" (จากบล็อก Joyent อันดับ 1 สำหรับคำหลัก“ การจัดการข้อผิดพลาด Node.JS”)

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


หมายเลข 5: ข้อผิดพลาด API เอกสารโดยใช้ Swagger

TL; DR: แจ้งให้ผู้โทร API ของคุณทราบว่าข้อผิดพลาดใดที่อาจกลับมาเพื่อให้พวกเขาสามารถจัดการกับสิ่งเหล่านี้ได้อย่างรอบคอบโดยไม่ต้องหยุดทำงาน สิ่งนี้มักจะทำกับกรอบเอกสาร REST API เช่น Swagger

มิฉะนั้น:ลูกค้า API อาจตัดสินใจผิดพลาดและรีสตาร์ทเพียงเพราะเขาได้รับข้อผิดพลาดที่เขาไม่เข้าใจ หมายเหตุ: ผู้เรียก API ของคุณอาจเป็นคุณ (โดยทั่วไปในสภาพแวดล้อมแบบไมโครไซต์)

คำพูดบล็อก: "คุณต้องแจ้งให้ผู้โทรทราบว่าเกิดข้อผิดพลาดเกิดขึ้นได้อย่างไร" (จากบล็อก Joyent อันดับ 1 สำหรับคำหลัก“ การบันทึก Node.JS”)

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


Number6: ปิดกระบวนการอย่างสง่างามเมื่อมีคนแปลกหน้าเข้ามาในเมือง

TL; DR:เมื่อเกิดข้อผิดพลาดที่ไม่ทราบสาเหตุ (ข้อผิดพลาดของนักพัฒนาดูหมายเลขแนวทางปฏิบัติที่ดีที่สุด # 3) - มีความไม่แน่นอนเกี่ยวกับความสมบูรณ์ของแอปพลิเคชัน การปฏิบัติทั่วไปแนะนำให้รีสตาร์ทกระบวนการอย่างระมัดระวังโดยใช้เครื่องมือ 'restarter' เช่น Forever และ PM2

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

ตัวอย่างโค้ด - การตัดสินใจว่าจะพังหรือไม่

//deciding whether to crash when an uncaught exception arrives
//Assuming developers mark known operational errors with error.isOperational=true, read best practice #3
process.on('uncaughtException', function(error) {
 errorManagement.handler.handleError(error);
 if(!errorManagement.handler.isTrustedError(error))
 process.exit(1)
});


//centralized error handler encapsulates error-handling related logic 
function errorHandler(){
 this.handleError = function (error) {
 return logger.logError(err).then(sendMailToAdminIfCritical).then(saveInOpsQueueIfCritical).then(determineIfOperationalError);
 }

 this.isTrustedError = function(error)
 {
 return error.isOperational;
 }

คำพูดบล็อก: "มีสามโรงเรียนคิดในการจัดการข้อผิดพลาด" (จากบล็อก jsrecipes)

... มีโรงเรียนหลักสามแห่งเกี่ยวกับการจัดการข้อผิดพลาด: 1. ปล่อยให้แอปพลิเคชันหยุดทำงานและเริ่มต้นใหม่ 2. จัดการข้อผิดพลาดที่เป็นไปได้ทั้งหมดและไม่ผิดพลาด 3. วิธีการสมดุลระหว่างสอง


Number7: ใช้ logger ที่เป็นผู้ใหญ่เพื่อเพิ่มการมองเห็นข้อผิดพลาด

TL; DR:ชุดเครื่องมือการบันทึกสำหรับผู้ใหญ่อย่าง Winston, Bunyan หรือ Log4J จะช่วยให้การค้นหาและทำความเข้าใจข้อผิดพลาดได้เร็วขึ้น ดังนั้นลืมเกี่ยวกับ console.log

ไม่เช่นนั้น: การอ่านผ่าน console.logs หรือด้วยตนเองผ่านไฟล์ข้อความยุ่ง ๆ โดยไม่ต้องใช้เครื่องมือในการสอบถามหรือเครื่องมือดูไฟล์บันทึกที่ดีอาจทำให้คุณยุ่งอยู่กับการทำงานจนถึงปลายปี

ตัวอย่างโค้ด - การดำเนินการ Winston logger

//your centralized logger object
var logger = new winston.Logger({
 level: 'info',
 transports: [
 new (winston.transports.Console)(),
 new (winston.transports.File)({ filename: 'somefile.log' })
 ]
 });

//custom code somewhere using the logger
logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' });

คำพูดบล็อก: "ช่วยระบุข้อกำหนดบางประการ (สำหรับคนตัดไม้):" (จากบล็อกที่แข็งแกร่ง)

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


หมายเลข 8: ค้นหาข้อผิดพลาดและการหยุดทำงานโดยใช้ผลิตภัณฑ์ APM

TL; DR:ผลิตภัณฑ์การตรวจสอบและประสิทธิภาพ (หรือที่เรียกว่า APM) ทำการวัด codebase หรือ API ของคุณในเชิงรุกเพื่อให้พวกเขาสามารถเน้นข้อผิดพลาดอัตโนมัติอย่างน่าอัศจรรย์ล่มและชิ้นส่วนที่ช้าซึ่งคุณหายไป

มิฉะนั้น:คุณอาจใช้ความพยายามอย่างมากในการวัดประสิทธิภาพ API และช่วงเวลาหยุดทำงานบางทีคุณอาจไม่เคยรู้เลยว่าส่วนใดของรหัสที่ช้าที่สุดของคุณภายใต้สถานการณ์ในโลกแห่งความเป็นจริงและสิ่งเหล่านี้ส่งผลต่อ UX อย่างไร

คำพูดบล็อก: "ส่วนผลิตภัณฑ์ APM" (จากบล็อก Yoni Goldberg)

"…ผลิตภัณฑ์ APM ประกอบด้วย 3 ส่วนหลัก: 1. การตรวจสอบเว็บไซต์หรือ API -บริการภายนอกที่ตรวจสอบสถานะการออนไลน์และประสิทธิภาพผ่านการร้องขอ HTTP อย่างต่อเนื่องสามารถตั้งค่าได้ในไม่กี่นาทีต่อไปนี้เป็นคู่แข่งที่เลือก: Pingdom, Uptime Robot และ New Relic 2 . เครื่องมือวัดรหัส -ตระกูลผลิตภัณฑ์ที่ต้องการฝังตัวแทนภายในแอปพลิเคชันเพื่อรับประโยชน์การตรวจจับโค้ดที่ช้า, สถิติการยกเว้น, การตรวจสอบประสิทธิภาพและอื่น ๆ อีกมากมายต่อไปนี้เป็นคู่แข่งที่เลือกไม่กี่: New Relic, App Dynamics 3กลุ่มผลิตภัณฑ์เหล่านี้มุ่งเน้นไปที่การอำนวยความสะดวกให้กับทีม ops ด้วยการวัดและเนื้อหาที่คัดสรรซึ่งจะช่วยให้สามารถอยู่ด้านบนสุดของประสิทธิภาพแอปพลิเคชันได้อย่างง่ายดาย ซึ่งมักจะเกี่ยวข้องกับการรวบรวมแหล่งข้อมูลหลายแห่ง (บันทึกแอปพลิเคชันบันทึกฐานข้อมูลบันทึกเซิร์ฟเวอร์ ฯลฯ ) และงานออกแบบแผงควบคุมด้านหน้า ต่อไปนี้เป็น contenders ที่เลือกไม่กี่: Datadog, Splunk "


ข้างต้นเป็นเวอร์ชั่นย่อ - ดูวิธีปฏิบัติที่ดีที่สุดและตัวอย่างเพิ่มเติมได้ที่นี่


30

คุณสามารถตรวจจับข้อยกเว้นที่ไม่ได้ตรวจสอบ แต่มีข้อ จำกัด ในการใช้งาน ดูhttp://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb

monit, foreverหรือupstartสามารถใช้ในการเริ่มต้นกระบวนการโหนดเมื่อเกิดปัญหา การปิดระบบที่ดีเยี่ยมเป็นสิ่งที่ดีที่สุดที่คุณคาดหวังได้ (เช่นบันทึกข้อมูลในหน่วยความจำทั้งหมดในเครื่องมือจัดการข้อยกเว้นที่ไม่ได้ตรวจสอบ)


4
+1 ลิงค์นี้มีประโยชน์ขอบคุณ ฉันยังคงมองหาแนวปฏิบัติที่ดีที่สุดและความหมายของ "การรีสตาร์ทอย่างสง่างาม" ในบริบทของ node.js
momo

ความเข้าใจของฉันเกี่ยวกับ "การรีสตาร์ทอย่างสง่างาม" ในบริบทนี้จะเป็นสิ่งที่ nponeccop แนะนำ: ให้กระบวนการตายและปล่อยให้สิ่งที่กำลังรันอยู่ในตอนแรกรีสตาร์ท
Ilkka

ขอบคุณมากสำหรับลิงค์! มีประโยชน์จริงๆ!
SatheeshJM

นี่คือคำตอบที่ดี อย่างไรก็ตามฉันไม่เห็นด้วยกับการส่งคืนข้อผิดพลาดในตัวอย่างแรกของคุณ การErrorคืนค่าทำให้ polymorphic ส่งคืนค่าซึ่งทำให้ความหมายของฟังก์ชันยุ่งเหยิงโดยไม่จำเป็น นอกจากนี้การดำน้ำโดย 0 คือการจัดการอยู่แล้วใน JavaScript โดยการให้Infinity, -Infinityหรือค่านิยมที่NaN พวกเขาสามารถตรวจสอบได้ด้วยtypeof === 'number' !isFinite(value)โดยทั่วไปฉันจะแนะนำไม่เคยส่งคืนข้อผิดพลาดจากฟังก์ชั่น ดีกว่าในแง่ของความชัดเจนของรหัสและการบำรุงรักษาที่จะโยนหรือส่งคืนค่าที่ไม่ใช่ polymorphic พิเศษที่มีความหมายที่สอดคล้องกัน
wprl

ลิงก์เสีย downforeveryoneorjustme.com/debuggable.com
Kev

13

โดเมน nodejsเป็นวิธีที่ทันสมัยที่สุดในการจัดการข้อผิดพลาดใน nodejs โดเมนสามารถจับทั้งข้อผิดพลาด / เหตุการณ์อื่น ๆ เช่นเดียวกับวัตถุที่โยนแบบดั้งเดิม โดเมนยังมีฟังก์ชันการทำงานสำหรับจัดการการเรียกกลับด้วยข้อผิดพลาดที่ส่งผ่านเป็นอาร์กิวเมนต์แรกผ่านวิธีการดักจับ

เช่นเดียวกับการจัดการข้อผิดพลาดแบบลอง / catch-style ปกติจะเป็นการดีที่สุดที่จะโยนข้อผิดพลาดเมื่อเกิดขึ้นและปิดกั้นพื้นที่ที่คุณต้องการแยกข้อผิดพลาดจากการส่งผลกระทบต่อส่วนที่เหลือของรหัส วิธีการ "บล็อก" พื้นที่เหล่านี้คือการเรียก domain.run ด้วยฟังก์ชั่นเป็นบล็อกของรหัสที่แยกได้

ในรหัสซิงโครนัสข้างต้นก็เพียงพอ - เมื่อเกิดข้อผิดพลาดให้คุณปล่อยให้มันผ่านหรือคุณจับมันและจัดการที่นั่นคืนค่าข้อมูลใด ๆ ที่คุณต้องการเปลี่ยนกลับ

try {  
  //something
} catch(e) {
  // handle data reversion
  // probably log too
}

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

var err = null;
var d = require('domain').create();
d.on('error', function(e) {
  err = e;
  // any additional error handling
}
d.run(function() { Fiber(function() {
  // do stuff
  var future = somethingAsynchronous();
  // more stuff

  future.wait(); // here we care about the error
  if(err != null) {
    // handle data reversion
    // probably log too
  }

})});

โค้ดด้านบนบางอันนั้นน่าเกลียด แต่คุณสามารถสร้างรูปแบบสำหรับตัวคุณเองเพื่อให้มันสวยกว่าเช่น:

var specialDomain = specialDomain(function() {
  // do stuff
  var future = somethingAsynchronous();
  // more stuff

  future.wait(); // here we care about the error
  if(specialDomain.error()) {
    // handle data reversion
    // probably log too
  } 
}, function() { // "catch"
  // any additional error handling
});

อัปเดต (2013-09):

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

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

ตัวอย่าง:

returnsAFuture().then(function() {
  console.log('1')
  return doSomething() // also returns a future

}).then(function() {
  console.log('2')
  throw Error("oops an error was thrown")

}).then(function() {
  console.log('3')

}).catch(function(exception) {
  console.log('handler')
  // handle the exception
}).done()

สิ่งนี้เลียนแบบการลองจับแบบปกติถึงแม้ว่าชิ้นส่วนจะเป็นแบบอะซิงโครนัสก็ตาม มันจะพิมพ์:

1
2
handler

โปรดทราบว่ามันไม่ได้พิมพ์ '3' เพราะมีข้อผิดพลาดเกิดขึ้นที่ขัดจังหวะการไหลนั้น

ดูที่สัญญาคราม:

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


ข้อกำหนดสัญญาที่เหมาะสมใน Javascript เรียกว่า Promises / A + คุณอาจจะเห็นรายการของการใช้งานที่นี่: github.com/promises-aplus/promises-spec/blob/master/... โปรดทราบว่าสัญญาที่ไม่เปิดเผย / A + นั้นใช้ไม่ได้ในทางปฏิบัติ - สัญญา / A + ยังคงทำให้เกิดปัญหามากมายสำหรับห้องสมุดในการแก้ปัญหาด้วยตนเอง อย่างไรก็ตามสิ่งที่จำเป็นอย่างยิ่งเช่นการเผยแพร่ข้อผิดพลาดที่คุณแสดงคำสั่งการดำเนินการที่กำหนดขึ้นและความปลอดภัยจากการล้นสแต็กรับประกัน
Esailija


11

ผมเขียนเกี่ยวกับเรื่องนี้เมื่อเร็ว ๆ นี้ที่http://snmaynard.com/2012/12/21/node-error-handling/ คุณลักษณะใหม่ของโหนดในรุ่น 0.8 คือโดเมนและช่วยให้คุณสามารถรวมรูปแบบการจัดการข้อผิดพลาดทั้งหมดไว้ในรูปแบบการจัดการที่ง่ายขึ้น คุณสามารถอ่านเกี่ยวกับพวกเขาในโพสต์ของฉัน

คุณยังสามารถใช้สิ่งต่างๆเช่นBugsnagเพื่อติดตามข้อยกเว้นที่ไม่ได้ตรวจสอบและได้รับแจ้งทางอีเมลห้องสนทนาหรือมีตั๋วที่สร้างขึ้นเพื่อการยกเว้นที่ไม่ได้ตรวจสอบ (ฉันเป็นผู้ร่วมก่อตั้ง Bugsnag)


2
โมดูลโดเมนเลิกใช้แล้วในขณะนี้ nodejs.org/api/domain.html
MattSidor

3

ฉันต้องการเพิ่มห้องสมุด Step.js ที่ช่วยให้คุณจัดการข้อยกเว้นโดยส่งผ่านไปยังฟังก์ชันขั้นตอนถัดไปเสมอ ดังนั้นคุณสามารถมีฟังก์ชั่นขั้นตอนสุดท้ายที่ตรวจสอบข้อผิดพลาดในขั้นตอนก่อนหน้าใด ๆ วิธีการนี้ช่วยให้การจัดการข้อผิดพลาดของคุณง่ายขึ้นมาก

ด้านล่างเป็นคำพูดจากหน้า GitHub:

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

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


3

อินสแตนซ์หนึ่งที่ใช้ try-catch อาจเหมาะสมเมื่อใช้ forEach loop มันเป็นแบบซิงโครนัส แต่ในเวลาเดียวกันคุณไม่สามารถใช้คำสั่ง return ในขอบเขตด้านในได้ คุณสามารถใช้วิธีลองและจับเพื่อส่งคืนวัตถุข้อผิดพลาดในขอบเขตที่เหมาะสมแทน พิจารณา:

function processArray() {
    try { 
       [1, 2, 3].forEach(function() { throw new Error('exception'); }); 
    } catch (e) { 
       return e; 
    }
}

มันเป็นการรวมกันของวิธีการที่อธิบายโดย @ balupton ด้านบน


แทนที่จะทิ้งข้อผิดพลาดนักพัฒนาบางคนแนะนำให้ใช้แนวคิดผลลัพธ์จาก Rust เพื่อส่งคืนตกลงหรือล้มเหลวเมื่อความล้มเหลวเป็นไปได้ว่าเป็นไปได้ สิ่งนี้ทำให้ความล้มเหลวแยกจากข้อผิดพลาดที่ไม่คาดคิด หนึ่งในการดำเนินงาน JS นี้คือR-ผล
joeytwiddle

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

1

หลังจากอ่านโพสต์เมื่อไม่นานมานี้ฉันสงสัยว่ามันปลอดภัยที่จะใช้โดเมนเพื่อจัดการข้อยกเว้นในระดับ api / function หรือไม่ ฉันต้องการใช้มันเพื่อทำให้การจัดการโค้ดยกเว้นในฟังก์ชั่น async แต่ละอันที่ฉันเขียนง่ายขึ้น ความกังวลของฉันคือการใช้โดเมนใหม่สำหรับแต่ละฟังก์ชั่นจะแนะนำค่าใช้จ่ายที่สำคัญ การบ้านของฉันดูเหมือนจะบ่งบอกว่ามีค่าใช้จ่ายน้อยที่สุดและประสิทธิภาพนั้นดีกว่ากับโดเมนมากกว่าลองดูในบางสถานการณ์

http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/


1

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

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

var log = bunyan.createLogger({
  name: 'myapp',
  streams: [
    {
      level: 'error',
      path: '/var/tmp/myapp-error.log'  // log ERROR to this file
    }
  ]
});

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

var raygunClient = new raygun.Client().init({ apiKey: 'your API key' });
raygunClient.send(theError);

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


โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.