概述阻塞和不阻塞

avatarplhDigital nomad

概述阻塞和不阻塞

这个概述了nodejs阻塞和非阻塞的区别。这个概述将会参考事件循环和libuv,但不需要实现了解这些主题。读者被假设有如下基础,明白js语言以及nodejs回调模式

  • I/O主要指与libuv支持的系统磁盘和lubuv支持的网络之间的交互。

阻塞

阻塞是当执行添加js在nodejs处理必须等待未阻塞的js操作完成。这发生是因为事件循环是不能继续执行js,当一个阻塞的操作被占用。 在Node.js,js是展示低效因为CPU密集型而不是等待非JavaScript操作,例如I/O,这个不是一般的参照去阻塞。同步方法在nodejs一般的库,用libuv,是大多数一般的被用来阻塞操作。本地模块也一样有阻塞的方法。 所有I/O方法在Node.js一般库提供异步版本,这个是不阻塞的,并且接受回调函数。一些方法也有阻塞对方,他们的名字以Sync结尾。

对比代码

阻塞方法执行同步和不阻塞方法执行异步。 使用文件系统模块作为一个例子,这个是一个同步文件读取:

const fs = require('fs');
const data = fa.readFileSync('/file.md');    // blocks here until file is read

下面是一个等价的异步方法:

const fs = require('fs');
fs.readFile('./README.md', 'utf-8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

第一个例子,看起来比第二个例子简单,但是一个坏处就算第二行阻塞了执行任何读取文件之后的代码,知道文件成功读取。注意:同步的版本如果发生错误,他将需要捕捉错误,或者让进程奔溃。在一部版本,可以自由决定如何报错。 让我们扩展这个例子

// 嗯这段同步的代码和上一个例子没有区别
const data = fs.readFileSync('./README.md', 'utf-8');
console.log(data);
console.log('finished');

然后,这里有一个相似的,但是不等价的异步函数代码,你会发现,finished会被在data之前打印出来

fs.readFile('./README.md', 'utf-8', (err, data) => {
  if (err) throw err;
  console.log(data);
});
console.log('finished');

上面第一个例子,打印将会被宰morework之前调用,在第二个例子,fs.readfile不阻塞js代码继续执行,所以会这样。在不等文件读取完成的情况下运行moreWork()的能力是允许更高吞吐量的关键设计的选择。

并发和吞吐量

js编译器在nodejs是单线程,所以并发指的是事件循环的容量来执行js回调函数,在完成其他工作之前。所有代码被预期以并发的形式运行必须允许事件循环继续允许作为没有js操作的像I/O形式继续运行。

作为一个例子,让我们考虑下面这个例子,每个请求对于web服务器花费50ms来完成并且其中的45ms是数据I/O,他们可以是异步的完成。选择不阻塞异步操作可以释放每个请求45ms来处理其他请求。这是一个通过选择使用异步的方法来操纵,这有着明显的容量差异。 事件循环是不一样的模型,对比很多其他语言,哪里可以创建额外的线程来处理并发工作。

混合阻塞和非阻塞代码是危险的

这里有一些模式,应该避免,当处理I/O。让我们看下面的例子: image

const fs = require('fs');
fs.readFile('/file.md', (err, data) => {
  if (err) throw err;
  console.log(data);
});
fs.unlinkSync('/file.md');

上述例子,fs.unlinkSync()像是会跑在fs.readFile()前面,他会删file.md文件,在他真正读取该文件之前,一个更好的方式,去写这个代码,是不阻塞的完成,并且保证去执行正确的顺序:

const fs = require('fs');
fs.readFile('/file.md', (readFileErr, data) => {
  if (readFileErr) throw readFileErr;
  console.log(data);
  fs.unlink('/file.md', (unlinkErr) => {
    if (unlinkErr) throw unlinkErr;
  });
});

上面位置不阻塞的调用fs.unlink()在回调fs.readFile()之内,这保证的正确的执行操作顺序。

reference

结论:nodejs好像真的就是一些特别有钱的人闲着没事玩出来的一个产物。但是请回头看看tensorflow.js这个目前在github上比较火的JavaScript 机器学习库。JavaScript正在发展,JavaScript不是玩具。