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

[NodeHero]3.理解异步编程

时间:2023-04-03 10:41:45 Node.js

com/node-hero-async-programming-in-node-js/在本章中,我将引导您了解异步编程的原理,并向您展示如何在JavaScript和节点.js。异步编程在传统的编程实践中,大多数I/O操作都是同步发生的。如果你想到Java,以及如何用Java读取文件,你会得到这样的代码:StringfileContent=IOUtils.toString(inputStream);}幕后发生了什么?主线程将被阻塞,直到文件被读取,这意味着在读取文件时不能做任何其他事情。要解决此问题并更好地利用CPU,您必须手动管理线程。如果有更多的阻塞操作,那么事件队列会变得更糟:(红色块表示进程何时被阻塞等待来自外部资源的响应,黑色块表示代码何时运行,绿色块表示应用程序的其余部分)针对这个问题,Node.js引入了异步编程模型。Node.js中的异步编程异步I/O是一种输入/输出处理形式,它允许其他处理继续进行,直到传输完成。在下面的示例中,我将展示一个简单的Node.js文件读写过程-同步和异步,以便向您展示通过避免阻塞应用程序可以实现的目标。让我们从一个简单的例子开始——用Node.js同步读取一个文件:constfs=require('fs')letcontenttry{content=fs.readFileSync('file.md','utf-8')}catch(ex){console.log(ex)}console.log(content)这里发生了什么?我们正在尝试使用fs模块的同步接口读取文件。它按预期工作-content变量将包含file.md的内容。这种方法的问题是Node.js会阻塞直到操作完成——这意味着它在读取文件时不能做任何事情。让我们看看如何解决它!我们已经看到,在JavaScript中,异步编程只能使用函数,函数是该语言的一等公民:函数可以像所有其他变量一样传递给其他函数。将其他函数作为参数的函数称为高阶函数。高阶函数最简单的例子如下:constnumbers=[2,4,1,5,4]functionisBiggerThanTwo(num){returnnum>2}numbers.filter(isBiggerThanTwo)在上面的例子中,我们将使用传递给过滤器函数的函数。这样我们就可以定义过滤逻辑了。这就是回调的诞生方式:如果你将一个函数作为参数传递给另一个函数,当另一个函数完成它的工作时,你可以在该函数内部调用传递的函数。不需要返回值,只需要用一个值调用另一个函数。这些所谓的错误优先回调是Node.js本身的核心——核心模块使用它,就像NPM中的大多数模块一样。constfs=require('fs')fs.readFile('file.md','utf-8',function(err,content){if(err){returnconsole.log(err)}console.log(content)})此处注意:错误处理:必须在回调中检测到错误,而不是在try-catch块中。无返回值:异步函数不返回值,但该值将传递给回调。让我们修改这个文件看看它是如何工作的:constfs=require('fs')console.log('startreadingafile...')fs.readFile('file.md','utf-8',function(err,content){if(err){console.log('读取文件时出错')returnconsole.log(err)}console.log(content)})console.log('文件结束')此脚本的输出将是:开始读取文件...读取文件时发生文件结尾错误如您所见,一旦我们开始读取文件,执行将继续,应用程序打印文件结尾.一旦文件读取完成,我们的回调只会被调用一次。这怎么可能?认识事件循环。事件循环事件循环是Node.js/JavaScript的核心——它负责调度异步操作。在深入研究之前,请确保您了解什么是事件驱动编程。事件驱动编程是一种编程范例,其中程序流由事件决定,例如用户操作(鼠标单击、按键)、传感器输出或来自其他程序/线程的消息。实际上,这意味着应用程序根据事件进行操作。此外,正如我们在第1章中了解到的,从开发人员的角度来看,Node.js是单线程的。这意味着无需处理线程和线程同步,Node.js就远离了这种复杂性。除您的代码外,所有内容都是并行执行的。要更深入地了解事件循环,请观看youtube上的视频。异步控制流既然您已经对JavaScript中异步编程的工作机制有了基本的了解,那么让我们来看几个如何组织代码的示例。Async.js为避免所谓的回调地狱,您可以做的一件事就是开始使用async.js。Async.js有助于组织应用程序结构并使流程控制更容易。下面我们看一个使用Async.js的简短示例,然后用Promises重写它。以下代码片段将状态映射到三个文件:async.parallel(['file1','file2','file3'],fs.stat,function(err,results){//现在结果是文件的状态数组})PromisesPromise对象用于延迟和异步计算。Promise表示尚未完成但将在未来执行的操作。在实践中,前面的例子可以重写如下:functionstats(file){returnnewPromise((resolve,reject)=>{fs.stat(file,(err,data)=>{if(err){reject(err)}resolve(data)})})}Promise.all([stats('file1'),stats('file2'),stats('file3')]).then((data)=>控制台.log(data)).catch((err)=>console.log(err))当然,如果你使用一个有Promise接口的方法,Promise的例子也会少很多行。下一步:您的第一个Node.js服务器在下一章中,您将学习如何启动您的第一个Node.jsHTTP服务器。