การจัดการกับปิรามิดของ node.js


9

ฉันเพิ่งเริ่มใช้โหนดและสิ่งหนึ่งที่ฉันสังเกตเห็นได้อย่างรวดเร็วก็คือการโทรกลับอย่างรวดเร็วสามารถสร้างการเยื้องระดับที่บ้าบิ่นได้อย่างไร:

doStuff(arg1, arg2, function(err, result) {
    doMoreStuff(arg3, arg4, function(err, result) {
        doEvenMoreStuff(arg5, arg6, function(err, result) {
            omgHowDidIGetHere();
        });
    });
});

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

การใช้ขอบเขตฟังก์ชั่นช่วยได้หรือไม่? ใส่ฟังก์ชั่นการโทรกลับทั้งหมดที่ต้องการเข้าถึงวัตถุ Global-ish ภายในฟังก์ชั่นที่ประกาศวัตถุนั้นดังนั้นมันจึงปิดลง?

function topLevelFunction(globalishObject, callback) {

    function doMoreStuffImpl(err, result) {
        doMoreStuff(arg5, arg6, function(err, result) {
            callback(null, globalishObject);
        });
    }

    doStuff(arg1, arg2, doMoreStuffImpl);
}

และอีกหลายเลเยอร์ ...

หรือมีกรอบ ฯลฯ เพื่อช่วยลดระดับของการเยื้องโดยไม่ต้องประกาศฟังก์ชั่นที่มีชื่อสำหรับการโทรกลับทุกครั้งหรือไม่ คุณจะจัดการกับปิรามิดโทรกลับได้อย่างไร


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

คำตอบ:


7

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

มีการใช้งาน "สัญญา" หลายครั้ง:


ตัวอย่างเช่นคุณสามารถเขียนการเรียกกลับที่ซ้อนกันนี้ใหม่

http.get(url.parse("http://test.com/"), function(res) {
    console.log(res.statusCode);
    http.get(url.parse(res.headers["location"]), function(res) {
        console.log(res.statusCode);
    });
});

ชอบ

httpGet(url.parse("http://test.com/")).then(function (res) {
    console.log(res.statusCode);
    return httpGet(url.parse(res.headers["location"]));
}).then(function (res) {
    console.log(res.statusCode);
});

แทนการโทรกลับในการติดต่อกลับa(b(c()))คุณโซ่ a().then(b()).then(c())"จากนั้น"


คำแนะนำที่นี่: http://howtonode.org/promises


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

1
โอเคขอโทษ ฉันได้เพิ่มตัวอย่างและข้อมูลเพิ่มเติม
Fabien Sa

3

เพื่อเป็นทางเลือกแทนสัญญาคุณควรดูyieldคำหลักร่วมกับฟังก์ชันตัวสร้างซึ่งจะนำมาใช้ใน EcmaScript 6 ทั้งสองมีอยู่ใน Node.js 0.11.x บิวด์วันนี้ แต่ต้องการให้คุณระบุ--harmonyธงเมื่อรันโหนด .js:

$ node --harmony app.js

การใช้โครงสร้างและไลบรารีเช่น TJ Holowaychuk's coช่วยให้คุณสามารถเขียนโค้ดแบบอะซิงโครนัสในสไตล์ที่ดูเหมือนรหัสซิงโครนัสแม้ว่ามันจะยังคงทำงานในแบบอะซิงโครนัสก็ตาม โดยพื้นฐานแล้วสิ่งเหล่านี้ร่วมกันใช้การสนับสนุนร่วมประจำสำหรับ Node.j

โดยทั่วไปสิ่งที่คุณต้องทำคือการเขียนฟังก์ชั่นเครื่องกำเนิดไฟฟ้าสำหรับรหัสที่เรียกใช้เนื้อหาแบบอะซิงโครนัสเรียกใช้เนื้อหาแบบอะซิงโครนัสในนั้น แต่นำหน้าด้วยyieldคำหลัก ดังนั้นในที่สุดรหัสของคุณดูเหมือน:

var run = function * () {
  var data = yield doSomethingAsync();
  console.log(data);
};

ในการเรียกใช้ฟังก์ชั่นเครื่องกำเนิดไฟฟ้านี้คุณต้องมีห้องสมุดเช่น บริษัท ที่กล่าวถึงก่อนหน้านี้ การโทรนั้นมีลักษณะดังนี้:

co(run);

หรือวางไว้ในบรรทัด:

co(function * () {
  // ...
});

โปรดทราบว่าจากฟังก์ชั่นเครื่องกำเนิดไฟฟ้าที่คุณสามารถเรียกใช้ฟังก์ชั่นเครื่องกำเนิดไฟฟ้าอื่น ๆ สิ่งที่คุณต้องทำคือนำหน้าพวกเขาด้วยyieldอีกครั้ง

สำหรับคำแนะนำในหัวข้อนี้ค้นหาคำต่างๆของ Google เช่นyield generators es6 async nodejsและคุณควรหาข้อมูลมากมาย ใช้เวลาสักครู่ก่อนที่จะชินกับมัน แต่เมื่อคุณได้รับแล้วคุณไม่ต้องการกลับไปที่เคย

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

ขอให้โชคดีและสนุก :-)!


0

ตอนนี้คุณมีแพ็คเกจasyncawaitพร้อมกับไวยากรณ์ที่ใกล้เคียงกับสิ่งที่ควรจะได้รับการสนับสนุนในอนาคตของawait& asyncในโหนด

โดยทั่วไปจะช่วยให้คุณสามารถเขียนโค้ดแบบอะซิงโครนัสที่ดูแบบซิงโครนัสลดระดับ LOC & เยื้องอย่างมากด้วยการแลกเปลี่ยนประสิทธิภาพการสูญเสียเล็กน้อย (หมายเลขของเจ้าของแพ็คเกจคือความเร็ว 79% เมื่อเทียบกับการโทรกลับ

ยังเป็นตัวเลือกที่ดีในการโทรออกจากนรก / ปิรามิดแห่งฝันร้าย IMO เมื่อการแสดงไม่ใช่ความกังวลหลักและสไตล์การเขียนแบบซิงโครนัสเหมาะสมกับความต้องการของโครงการของคุณมากขึ้น

ตัวอย่างพื้นฐานจากเอกสารแพคเกจ:

var foo = async (function() {
    var resultA = await (firstAsyncCall());
    var resultB = await (secondAsyncCallUsing(resultA));
    var resultC = await (thirdAsyncCallUsing(resultB));
    return doSomethingWith(resultC);
});
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.