วิธีเขียนฟังก์ชันอะซิงโครนัสสำหรับ Node.js


114

ฉันได้พยายามค้นคว้าว่าควรเขียนฟังก์ชันอะซิงโครนัสอย่างไร หลังจากไถเอกสารมากมายแล้วก็ยังไม่ชัดเจนสำหรับฉัน

ฉันจะเขียนฟังก์ชันอะซิงโครนัสสำหรับโหนดได้อย่างไร ฉันจะใช้การจัดการเหตุการณ์ข้อผิดพลาดอย่างถูกต้องได้อย่างไร

อีกวิธีในการถามคำถามของฉันคือฉันจะตีความฟังก์ชันต่อไปนี้ได้อย่างไร

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

นอกจากนี้ฉันพบคำถามนี้ใน SO ("ฉันจะสร้างฟังก์ชันอะซิงโครนัสแบบไม่บล็อกใน node.js ได้อย่างไร") น่าสนใจ ฉันรู้สึกว่ายังไม่ได้รับคำตอบ


14
นั่นเป็นเหตุผลที่ฉันถาม ฉันไม่เห็นว่าฟังก์ชันเหล่านี้แตกต่างกันอย่างไร
Kriem

ฉันขอแนะนำให้คุณดูsetTimeoutและsetIntervalในเบราว์เซอร์ที่คุณชื่นชอบและเล่นกับพวกเขาด้วย หรือ ajax callbacks (อาจเป็นสิ่งที่ใกล้เคียงที่สุดกับประสบการณ์การใช้งานโหนด) หรือผู้ฟังเหตุการณ์สำหรับสิ่งที่คุณคุ้นเคยเช่นเหตุการณ์คลิกและโหลด โมเดลอะซิงโครนัสมีอยู่แล้วในเบราว์เซอร์และเป็นแบบเดียวกันทุกประการในโหนด
davin

@davin - เดาว่าฉันไม่เข้าใจโมเดลอะซิงโครนัสอย่างเต็มที่
Kriem

@Kriem ฉันได้ตอบบางสิ่งเมื่อวานนี้ซึ่งอาจช่วยได้: stackoverflow.com/questions/6883648/…ไม่ใช่คำตอบสำหรับคำถามของคุณ แต่อยู่ในหัวข้อ ลองอ่านคำถามและคำตอบที่นั่นและลองใช้รหัสเพื่อพยายามทำความเข้าใจกับสิ่งที่เกิดขึ้น
davin

2
@Raynos คำจำกัดความของ "ฟังก์ชันอะซิงโครนัส" คืออะไร?
Anderson Green

คำตอบ:


85

ดูเหมือนว่าคุณจะสับสนกับ IO แบบอะซิงโครนัสกับฟังก์ชันอะซิงโครนัส node.js ใช้ IO ที่ไม่ปิดกั้นแบบอะซิงโครนัสเนื่องจาก IO ที่ไม่บล็อกนั้นดีกว่า วิธีที่ดีที่สุดในการทำความเข้าใจคือไปดูวิดีโอโดย ryan dahl

ฉันจะเขียนฟังก์ชันอะซิงโครนัสสำหรับโหนดได้อย่างไร

เพียงแค่เขียนฟังก์ชั่นปกติข้อแตกต่างเพียงอย่างเดียวคือไม่ได้ดำเนินการทันที แต่ส่งผ่านไปรอบ ๆ เป็นการเรียกกลับ

ฉันจะใช้การจัดการเหตุการณ์ข้อผิดพลาดอย่างถูกต้องได้อย่างไร

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

database.query('something', function(err, result) {
  if (err) handle(err);
  doSomething(result);
});

เป็นรูปแบบทั่วไป.

on('error')อีกรูปแบบที่พบบ่อยคือ ตัวอย่างเช่น

process.on('uncaughtException', function (err) {
  console.log('Caught exception: ' + err);
});

แก้ไข:

var async_function = function(val, callback){
    process.nextTick(function(){
        callback(val);
    });
};

ฟังก์ชันข้างต้นเมื่อเรียกเป็น

async_function(42, function(val) {
  console.log(val)
});
console.log(43);

จะพิมพ์42ไปยังคอนโซลแบบอะซิงโครนัส โดยเฉพาะอย่างยิ่งการเริ่มทำงานprocess.nextTickหลังจากเหตุการณ์ปัจจุบัน callstack ว่างเปล่า กองการโทรนั้นว่างเปล่าหลังจากasync_functionและconsole.log(43)รันแล้ว เราจึงพิมพ์ 43 ตามด้วย 42

คุณควรอ่านข้อมูลเกี่ยวกับการวนซ้ำเหตุการณ์


ฉันเคยเห็นวิดีโอ Dahl แต่ดูเหมือนฉันจะไม่เข้าใจในเรื่องที่ฉันกลัว :(
Kriem

1
@Kriem ดูคำตอบที่อัปเดตและอ่านเกี่ยวกับเหตุการณ์ที่เกิดขึ้น
Raynos

1
ขอบคุณข้อมูลเชิงลึก ตอนนี้ฉันตระหนักมากขึ้นถึงสิ่งที่ฉันขาดความรู้ :) ตัวอย่างสุดท้ายของคุณช่วยได้
Kriem

ฉันคิดว่าคำพูดของคุณเกี่ยวกับ IO แบบอะซิงโครนัสนั้น "ดีกว่า" นั้นกว้างเกินไป ในแง่นี้ใช่ แต่โดยรวมแล้วอาจไม่เป็นเช่นนั้น
Jake B

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

9

เพียงแค่ผ่านการโทรกลับไม่เพียงพอ คุณต้องใช้ตัวตั้งค่าเพื่อสร้างฟังก์ชัน async

ตัวอย่าง: ไม่ใช่ฟังก์ชัน async:

function a() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };
  b();
};

function b() {
  var a = 0;    
  for(i=0; i<10000000; i++) {
    a++;
  };    
  c();
};

function c() {
  for(i=0; i<10000000; i++) {
  };
  console.log("async finished!");
};

a();
console.log("This should be good");

หากคุณจะเรียกใช้ตัวอย่างข้างต้นสิ่งนี้จะดีจะต้องรอจนกว่าฟังก์ชันเหล่านั้นจะทำงานเสร็จ

ฟังก์ชัน Pseudo multithread (async):

function a() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };
    b();
  }, 0);
};

function b() {
  setTimeout ( function() {
    var a = 0;  
    for(i=0; i<10000000; i++) {
      a++;
    };  
    c();
  }, 0);
};

function c() {
  setTimeout ( function() {
    for(i=0; i<10000000; i++) {
    };
    console.log("async finished!");
  }, 0);
};

a();
console.log("This should be good");

อันนี้จะ async trully สิ่งนี้ควรจะดีจะได้รับการเขียนก่อน async จะเสร็จสิ้น



3

หากคุณรู้ว่าฟังก์ชันส่งคืนคำสัญญาฉันขอแนะนำให้ใช้คุณลักษณะ async / await ใหม่ใน JavaScript ทำให้ไวยากรณ์ดูซิงโครนัส แต่ทำงานแบบอะซิงโครนัส เมื่อคุณเพิ่มasyncคำสำคัญลงในฟังก์ชันจะช่วยให้คุณawaitสัญญาในขอบเขตนั้น:

async function ace() {
  var r = await new Promise((resolve, reject) => {
    resolve(true)
  });

  console.log(r); // true
}

หากฟังก์ชันไม่ส่งคืนคำสัญญาฉันขอแนะนำให้รวมไว้ในสัญญาใหม่ที่คุณกำหนดจากนั้นแก้ไขข้อมูลที่คุณต้องการ:

function ajax_call(url, method) {
  return new Promise((resolve, reject) => {
    fetch(url, { method })
    .then(resp => resp.json())
    .then(json => { resolve(json); })
  });
}

async function your_function() {
  var json = await ajax_call('www.api-example.com/some_data', 'GET');
  console.log(json); // { status: 200, data: ... }
}

บรรทัดล่าง: ใช้ประโยชน์จากพลังแห่งคำสัญญา


สิ่งที่ต้องจำไว้คือเนื้อหาของสัญญายังคงดำเนินการพร้อมกัน
shadow0359

2

ลองสิ่งนี้ใช้ได้กับทั้งโหนดและเบราว์เซอร์

isNode = (typeof exports !== 'undefined') &&
(typeof module !== 'undefined') &&
(typeof module.exports !== 'undefined') &&
(typeof navigator === 'undefined' || typeof navigator.appName === 'undefined') ? true : false,
asyncIt = (isNode ? function (func) {
  process.nextTick(function () {
    func();
  });
} : function (func) {
  setTimeout(func, 5);
});

18
4 downvotes และไม่มีความคิดเห็นเชิงสร้างสรรค์แม้แต่รายการเดียว .. : \
Omer

6
@Omer นั่นคือชีวิตบน SO
Piece Digital

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

0

ฉันใช้เวลาหลายชั่วโมงเกินไปสำหรับงานดังกล่าวใน node.js ฉันเป็นคนส่วนหน้าเป็นหลัก

ฉันพบว่าสิ่งนี้ค่อนข้างสำคัญเนื่องจากวิธีการโหนดทั้งหมดจัดการแบบไม่สัมพันธ์กับการเรียกกลับและเปลี่ยนเป็น Promise จะดีกว่าที่จะจัดการกับมัน

ฉันแค่ต้องการแสดงผลลัพธ์ที่เป็นไปได้เอนเอียงและอ่านง่ายขึ้น ใช้ ECMA-6 กับ async คุณสามารถเขียนแบบนี้ได้

 async function getNameFiles (dirname) {
  return new Promise((resolve, reject) => {
    fs.readdir(dirname, (err, filenames) => {
      err !== (undefined || null) ? reject(err) : resolve(filenames)
    })
  })
}

(undefined || null)สำหรับrepl (อ่านเหตุการณ์พิมพ์ห่วง) สถานการณ์การใช้งานไม่ได้กำหนดยัง

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