当前位置: 首页 > 后端技术 > Node.js

纤维、事件循环和流星

时间:2023-04-03 16:12:54 Node.js

Fibers、EventLoop和Meteor写在前面:我刚开始使用Meteor,看到官方文档中有一句话InMeteor,yourservercoderunsinasinglethreadperrequest。一开始因为对nodejs(单线程)的了解比较浅,不是很了解,所以找了一些资料。这篇文章解释的很清楚很到位。虽然是老文章(2013),但还是打算翻译一下学习一下。/meteorhacks.com/fibers-eventloop-and-meteor/Meteor使用Fibers来实现许多重要的特性。事实上,Meteor的流行很可能是由于使用了Fibers,尽管在深入了解Meteor之前你可能不会意识到这件事。理解Fibers的工作原理以及与Meteor的关系仍然有些困难。但是一旦理解了,将有助于我们对Meteor的内部工作原理有一个更清晰的认识。作者注:Fibers原本不在ProMeteor话题讨论列表中,但既然有人问了,我决定写这篇文章,让我们开始吧!事件循环和Node.jsMeteor基于Node.js,所以我们不能忘记Node。js的EventLoop(事件循环)。Node.js虽然是单线程运行,但是得益于事件循环和事件驱动模式,I/O操作(主要是网络请求和硬盘读写)不会阻塞程序的执行。而是提供一个回调函数在I/O操作结束后调用,然后继续运行程序。下面是两个伪代码示例,分别代表两个不同的任务);//定义函数.functionfetchTwitterFollowers(username){TwitterAPI.getProfile(username,function(){Model.setFollowers(profile.username,profile.followers,function(){console.log('配置文件已保存!');});});}functioncreateThumbnail(imageLocation,newLocation){File.getFile(imageLocation,function(err,fileData){varnewImage=ImageModule.resize(fileData);File.saveFile(newLocation,function(){console.log('imagesaved');});});}下面我们来看一下上面两个方法执行的时序绿色的是fetchTwitterFollowers任务,橙色的是createThumbnail。深色代表CPU时间,浅色代表I/O时间。蓝色条代表任务队列的等待时间,红色条代表空闲时间(CPUidle)观察上图可以发现一些有趣的信息。任务以不确定的顺序执行,I/O操作花费的时间不确定,并且它们不会阻止其他程序的执行。从上面的例子可以看出,ImageModule.resize不需要等待Twitter.getProfile执行。CPU占用确实会阻塞其他任务的执行。在上图的中间区域,可以看到蓝色条代表虽然TwitterAPI.getProfileI/O操作已经完成但是Model.setFollowers仍然不能执行。这是因为ImageModule.resize已经占用了CPU,所以阻塞了事件循环。如前所述,Node.js在单线程上运行。这也是Node.js不适合做一些图像处理、视频编码等CPU密集型场景的原因。您还可以看到三个红色条表示CPU空闲事件。如果我们的示例有其他任务,则使用这些时间Go执行。纤维现在您了解了事件循环的工作原理以及它为何如此高效。但是你还是不能忽视一个问题:回调函数。回调函数(或回调模式)使Node.js代码难以推理(或描述)回调沼泽)。错误处理和回调嵌套使代码难以编写,它们的存在使代码更难维护和扩展.这就是为什么Node.js这么难学(也很难用)的原因。幸运的是,有几种技术可以用来克服这个问题。比如Fibers、Promises、基于Generator的协程等。Meteor在底层使用Fibers。这样就基本封装了上层的API。在深入探讨之前,让我们先了解一下Fibers的工作原理。纤程在事件循环上提供了一个抽象层,允许我们按顺序执行任务(或方法)。让我们可以摆脱回调模式来编写异步代码。我们从两种模式的本质——异步高效和同步模式来思考写代码。之后Fibers会帮我们处理事件循环。如果使用得当,纤维会非常强大(Meteor用得很好)。此外,使用光纤引起的开销可以忽略不计。Meteor如何使用Fibers?Meteor在其API上抽象了Fibers,使我们能够避免回调模式。最好的事情是,当您编写避免回调模式的代码时,您甚至没有意识到您正在使用Fibers,它就可以正常工作。Meteor为每个客户端请求(DDP请求)创建一个Fiber。默认情况下,对于每个客户端Meteor一次只会处理一个请求,也就是说每次只会为每个客户端生成一个Fiber。但这可以改变。纤维是Meteor如此受欢迎的原因之一。因为它可以让我们的Node.js应用脱离回调模式,这会引来很多讨厌回调模式的开发者。如何在Meteor中使用异步方法Meteor的API并不能100%满足我们的需求,有时候我们需要使用npm模块来处理一些事情。当不使用回调时,在这种情况下我们应该怎么做?例如,假设您需要使用Github的npm模块来请求用户信息。这个过程需要在一个MeteorMethod中完成,最后我们需要从这个Method中返回这个信息。OK,我们来尝试实现这个需求varGithubAPI=Meteor.require('github');varghapi=newGithubAPI({版本:“3.0.0”});Meteor.methods({getProfile:function(username){ghapi.user.getFrom({user:username},function(err,profile){//如何返回?});//我们需要从这里返回配置文件。}});我们不能像上面那样使用回调。没有办法在回调中返回用户配置文件,因为Meteor的Method不会等待回调再次执行。现在我们需要学习如何使用Fibers来处理这种情况?或者有更好的选择吗?Meteor已经考虑到了这种情况,并为我们提供了一个简单的API来处理它。这个目前还没有出现在文档中(译者注:这是一篇老文章,现在文档中可以找到相应的说明。详见http://docs.meteor.com/api/core.html#Meteor-wrapAsync,这里是如何使用它。作者注:meteor-npm也有一系列与npm模块一起工作的异步实用程序。functiongetUserProfile(req,callback){ghapi.user.getFrom(req,callback);}varwrappedGetProfile=Meteor._wrapAsync(getUserProfile);Meteor.methods({getProfile:function(username){returnwrappedGetProfile({user:username});}});上面的代码很容易理解,我们包装ghapi.user.getwith一个方法,然后使用Meteor._wrapAsync调用这个方法来通知Fibers。然后我们可以使用上面的wrappedGetProfile在其他使用方法和MeteorAPI中获取用户信息。如果你知道bind,可以使用下面的代码来实现同样的效果varwrappedGetProfile=Meteor._wrapAsync(ghapi.user.getFrom.bind(ghapi.user));现在你终于对事件循环、Fibers以及Meteor如何使用Fibers有了更好的理解。您还知道如何通过Meteor._wrapAsync来使用异步方法。是时候应用这些知识来增强您的应用程序了。如果你想了解更多关于Fib的信息呃,看看下面列出的来自EventedMind的非常好的视频。使用FuturesMeteor._wrapAsyncUnderstanding事件循环异步和纤程引入纤程