Rumah > hujung hadapan web > tutorial js > Menganalisis panggilan balik dan corak reka bentuk kod dalam Node.js pengaturcaraan tak segerak_node.js

Menganalisis panggilan balik dan corak reka bentuk kod dalam Node.js pengaturcaraan tak segerak_node.js

WBOY
Lepaskan: 2016-05-16 15:15:13
asal
1287 orang telah melayarinya

Titik jualan terbesar NodeJS - mekanisme acara dan IO tak segerak, tidak telus kepada pembangun. Pembangun perlu menulis kod secara tidak segerak untuk memanfaatkan titik jualan ini, yang telah dikritik oleh beberapa lawan NodeJS. Tetapi tidak kira apa, pengaturcaraan tak segerak sememangnya ciri terbesar NodeJS Tanpa menguasai pengaturcaraan tak segerak, anda tidak boleh mengatakan bahawa anda telah benar-benar mempelajari NodeJS. Bab ini akan memperkenalkan pelbagai pengetahuan berkaitan pengaturcaraan tak segerak.

Dalam kod, manifestasi langsung pengaturcaraan tak segerak ialah panggilan balik. Pengaturcaraan tak segerak bergantung pada panggilan balik, tetapi tidak boleh dikatakan bahawa program menjadi tak segerak selepas menggunakan panggilan balik. Mula-mula kita boleh melihat kod berikut.

function heavyCompute(n, callback) {
 var count = 0,
  i, j;

 for (i = n; i > 0; --i) {
  for (j = n; j > 0; --j) {
   count += 1;
  }
 }

 callback(count);
}

heavyCompute(10000, function (count) {
 console.log(count);
});

console.log('hello');

Salin selepas log masuk
100000000
hello
Salin selepas log masuk

Seperti yang anda lihat, fungsi panggil balik dalam kod di atas masih dilaksanakan sebelum kod berikutnya. JS sendiri berjalan dalam satu utas, dan adalah mustahil untuk menjalankan kod lain sebelum sekeping kod selesai dijalankan, jadi tiada konsep pelaksanaan tak segerak.

Walau bagaimanapun, jika fungsi yang dilakukan adalah untuk mencipta urutan atau proses lain, lakukan sesuatu selari dengan utas utama JS, dan maklumkan utas utama JS apabila perkara itu selesai, maka situasinya berbeza. Mari kita lihat kod berikut.

setTimeout(function () {
 console.log('world');
}, 1000);

console.log('hello');

Salin selepas log masuk
hello
world
Salin selepas log masuk


Kali ini anda boleh melihat bahawa fungsi panggil balik dilaksanakan selepas kod berikutnya. Seperti yang dinyatakan di atas, JS sendiri adalah satu-benang dan tidak boleh dilaksanakan secara tak segerak Oleh itu, kita boleh berfikir bahawa fungsi khas yang disediakan oleh persekitaran berjalan di luar spesifikasi JS seperti setTimeout lakukan adalah untuk mencipta benang selari dan kembali dengan serta-merta. JS master to Proses kemudiannya boleh melaksanakan kod berikutnya dan melaksanakan fungsi panggil balik selepas menerima pemberitahuan daripada proses selari. Sebagai tambahan kepada yang biasa seperti setTimeout dan setInterval, fungsi tersebut juga termasuk API tak segerak yang disediakan oleh NodeJS seperti fs.readFile.

Selain itu, kami masih kembali kepada fakta bahawa JS berjalan dalam satu urutan, yang menentukan bahawa JS tidak boleh melaksanakan kod lain, termasuk fungsi panggil balik, sebelum melaksanakan sekeping kod. Dalam erti kata lain, walaupun jika utas selari menyelesaikan kerjanya dan memberitahu utas utama JS untuk melaksanakan fungsi panggil balik, fungsi panggil balik tidak akan memulakan pelaksanaan sehingga utas utama JS melahu. Berikut adalah contoh sedemikian.

function heavyCompute(n) {
 var count = 0,
  i, j;

 for (i = n; i > 0; --i) {
  for (j = n; j > 0; --j) {
   count += 1;
  }
 }
}

var t = new Date();

setTimeout(function () {
 console.log(new Date() - t);
}, 1000);

heavyCompute(50000);

Salin selepas log masuk
8520
Salin selepas log masuk


Seperti yang anda lihat, masa pelaksanaan sebenar fungsi panggil balik yang sepatutnya dipanggil selepas 1 saat telah sangat tertangguh kerana utas utama JS sedang sibuk menjalankan kod lain.

Corak Reka Bentuk Kod
Pengaturcaraan tak segerak mempunyai banyak corak reka bentuk kod yang unik Untuk mencapai fungsi yang sama, kod yang ditulis dalam mod segerak dan mod tak segerak akan sangat berbeza. Beberapa corak biasa diperkenalkan di bawah.

Nilai pulangan fungsi
Ia adalah keperluan yang sangat biasa untuk menggunakan output satu fungsi sebagai input fungsi lain Dalam mod segerak, kod biasanya ditulis seperti berikut:

var output = fn1(fn2('input'));
// Do something.
Salin selepas log masuk

Dalam mod tak segerak, memandangkan hasil pelaksanaan fungsi tidak dihantar melalui nilai pulangan, tetapi melalui fungsi panggil balik, kod biasanya ditulis dengan cara berikut:

fn2('input', function (output2) {
 fn1(output2, function (output1) {
  // Do something.
 });
});
Salin selepas log masuk

Seperti yang anda lihat, kaedah ini ialah satu fungsi panggil balik yang bersarang dalam satu fungsi panggil balik Jika terdapat terlalu banyak, ia adalah mudah untuk menulis kod berbentuk >.

Lintas tatasusunan
Apabila melintasi tatasusunan, ia juga merupakan keperluan biasa untuk menggunakan fungsi untuk melaksanakan beberapa pemprosesan pada ahli data mengikut turutan. Jika fungsi dilaksanakan secara serentak, kod berikut biasanya akan ditulis:

var len = arr.length,
 i = 0;

for (; i < len; ++i) {
 arr[i] = sync(arr[i]);
}

// All array items have processed.

Salin selepas log masuk

Jika fungsi dilaksanakan secara tidak segerak, kod di atas tidak dapat menjamin bahawa semua ahli tatasusunan telah diproses selepas gelung tamat. Jika ahli tatasusunan mesti diproses secara bersiri satu demi satu, kod tak segerak biasanya ditulis seperti berikut:

(function next(i, len, callback) {
 if (i < len) {
  async(arr[i], function (value) {
   arr[i] = value;
   next(i + 1, len, callback);
  });
 } else {
  callback();
 }
}(0, arr.length, function () {
 // All array items have processed.
}));
Salin selepas log masuk

Seperti yang anda lihat, kod di atas hanya lulus dalam ahli tatasusunan seterusnya dan memulakan pusingan pelaksanaan seterusnya selepas fungsi tak segerak dilaksanakan sekali dan mengembalikan hasil pelaksanaan Sehingga semua ahli tatasusunan diproses, pelaksanaan kod berikutnya dicetuskan melalui panggilan balik.

Jika ahli tatasusunan boleh diproses secara selari, tetapi kod berikutnya masih memerlukan semua ahli tatasusunan diproses sebelum mereka boleh dilaksanakan, kod tak segerak akan dilaraskan kepada bentuk berikut:

(function (i, len, count, callback) {
 for (; i < len; ++i) {
  (function (i) {
   async(arr[i], function (value) {
    arr[i] = value;
    if (++count === len) {
     callback();
    }
   });
  }(i));
 }
}(0, arr.length, 0, function () {
 // All array items have processed.
}));
Salin selepas log masuk

Seperti yang anda lihat, berbanding dengan versi traversal bersiri tak segerak, kod di atas memproses semua ahli tatasusunan secara selari dan menggunakan pemboleh ubah pembilang untuk menentukan bila semua ahli tatasusunan telah diproses.

Pengendalian pengecualian
Mekanisme penangkapan dan pengendalian pengecualian yang disediakan oleh JS sendiri - cuba..tangkap.., hanya boleh digunakan untuk kod yang dilaksanakan secara serentak. Di bawah adalah contoh.

function sync(fn) {
 return fn();
}

try {
 sync(null);
 // Do something.
} catch (err) {
 console.log('Error: %s', err.message);
}

Salin selepas log masuk
Error: object is not a function
Salin selepas log masuk
Salin selepas log masuk

Seperti yang anda lihat, pengecualian akan menggelembung di sepanjang laluan pelaksanaan kod sehingga ia ditangkap apabila ia menemui pernyataan percubaan pertama. Walau bagaimanapun, oleh kerana fungsi tak segerak mengganggu laluan pelaksanaan kod, apabila pengecualian dijana semasa dan selepas pelaksanaan fungsi tak segerak menggelembung ke lokasi di mana laluan pelaksanaan terganggu, jika tiada kenyataan cuba ditemui, ia akan dilemparkan sebagai pengecualian global . Di bawah adalah contoh.

function async(fn, callback) {
 // Code execution path breaks here.
 setTimeout(function () {
  callback(fn());
 }, 0);
}

try {
 async(null, function (data) {
  // Do something.
 });
} catch (err) {
 console.log('Error: %s', err.message);
}

Salin selepas log masuk
/home/user/test.js:4
  callback(fn());
     ^
TypeError: object is not a function
 at null._onTimeout (/home/user/test.js:4:13)
 at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
Salin selepas log masuk

因为代码执行路径被打断了,我们就需要在异常冒泡到断点之前用 try 语句把异常捕获住,并通过回调函数传递被捕获的异常。于是我们可以像下边这样改造上边的例子。

function async(fn, callback) {
 // Code execution path breaks here.
 setTimeout(function () {
  try {
   callback(null, fn());
  } catch (err) {
   callback(err);
  }
 }, 0);
}

async(null, function (err, data) {
 if (err) {
  console.log('Error: %s', err.message);
 } else {
  // Do something.
 }
});

Salin selepas log masuk
Error: object is not a function
Salin selepas log masuk
Salin selepas log masuk

可以看到,异常再次被捕获住了。在 NodeJS 中,几乎所有异步 API 都按照以上方式设计,回调函数中第一个参数都是 err。因此我们在编写自己的异步函数时,也可以按照这种方式来处理异常,与 NodeJS 的设计风格保持一致。

有了异常处理方式后,我们接着可以想一想一般我们是怎么写代码的。基本上,我们的代码都是做一些事情,然后调用一个函数,然后再做一些事情,然后再调用一个函数,如此循环。如果我们写的是同步代码,只需要在代码入口点写一个 try 语句就能捕获所有冒泡上来的异常,示例如下。

function main() {
 // Do something.
 syncA();
 // Do something.
 syncB();
 // Do something.
 syncC();
}

try {
 main();
} catch (err) {
 // Deal with exception.
}

Salin selepas log masuk

但是,如果我们写的是异步代码,就只有呵呵了。由于每次异步函数调用都会打断代码执行路径,只能通过回调函数来传递异常,于是我们就需要在每个回调函数里判断是否有异常发生,于是只用三次异步函数调用,就会产生下边这种代码。

function main(callback) {
 // Do something.
 asyncA(function (err, data) {
  if (err) {
   callback(err);
  } else {
   // Do something
   asyncB(function (err, data) {
    if (err) {
     callback(err);
    } else {
     // Do something
     asyncC(function (err, data) {
      if (err) {
       callback(err);
      } else {
       // Do something
       callback(null);
      }
     });
    }
   });
  }
 });
}

main(function (err) {
 if (err) {
  // Deal with exception.
 }
});

Salin selepas log masuk

可以看到,回调函数已经让代码变得复杂了,而异步方式下对异常的处理更加剧了代码的复杂度。

Label berkaitan:
sumber:php.cn
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan