资讯专栏INFORMATION COLUMN

【Node Hero】3. 理解异步编程

kevin / 941人阅读

摘要:异步编程在传统编程实践中,大多数操作都是同步发生的。中的异步编程异步是一种输入输出处理的形式,它允许在传输完成之前,其它处理能继续进行。

本文转载自:众成翻译
译者:网络埋伏纪事
链接:http://www.zcfy.cc/article/1759
原文:https://blog.risingstack.com/node-hero-async-programming-in-node-js/

本章我将指导你学习异步编程的原理,并向你展示如何在 JavaScript 和 Node.js 中实现异步编程。

异步编程

在传统编程实践中,大多数 I/O 操作都是同步发生的。如果想想 Java,想想如何用 Java 读取一个文件,你会得到下面这样的代码:

try(FileInputStream inputStream = new FileInputStream("foo.txt")) {  
    Session IOUtils;
    String fileContent = IOUtils.toString(inputStream);
}

这背后发生了什么?主线程会被阻塞,直到文件读完,这意味着读文件的同时其它什么事情都做不了。要解决此问题,更好利用 CPU,就不得不手动管理线程。

如果有更多阻塞操作,那么事件队列就变得更糟糕:

(红色块表示在进程等待外部资源的响应而被阻塞时,黑色块表示在代码运行时,绿色块表示应用的其余部分)

为解决这个问题,Node.js 引入了一种异步编程模型。

Node.js 中的异步编程

异步 I/O 是一种输入/输出处理的形式,它允许在传输完成之前,其它处理能继续进行。

在如下的示例中,我将展示 Node.js 中一个简单的文件读写过程 - 同时采用同步和异步的方式,目的是向你展示通过避免阻塞应用程序,能实现什么。

下面我们先从一个简单的示例开始 - 以同步的方式用 Node.js 读一个文件:

const fs = require("fs")  
let content  
try {  
  content = fs.readFileSync("file.md", "utf-8")
} catch (ex) {
  console.log(ex)
}
console.log(content)  

这里刚刚发生了什么?我们试图用 fs 模块的同步接口读一个文件。它按预期方式工作 - content 变量会包含 file.md 的内容。这种方式的问题是,Node.js 会被阻塞,直到操作完成 - 也就是说在文件正在被读取时,它什么事都做不了。

下面我们看看如何修复!

我们直到,在 JavaScript 中,异步编程只能用函数这个该语言的一等公民来实现:函数可以像所有其它变量一样传给其它函数。将其它函数作为参数的函数被称为高阶函数。

如下是一个高阶函数的最简单示例:

const numbers = [2,4,1,5,4]

function isBiggerThanTwo (num) {  
  return num > 2
}

numbers.filter(isBiggerThanTwo)  

在上例中,我们将一个函数传递给 filter 函数。通过这种方式我们可以定义过滤的逻辑。

这就是回调诞生的方式:如果你把一个函数传递给另一个函数作为参数,那么就可以在另一个函数完成任务时,在该函数内调用传进来的函数。不需要返回值,只用值调用另一个函数。

这些所谓错误优先(error-first)的回调是 Node.js 本身的核心 - 核心模块用了它,大多数 NPM 中的模块也是。

const fs = require("fs")  
fs.readFile("file.md", "utf-8", function (err, content) {  
  if (err) {
    return console.log(err)
  }

  console.log(content)
})

这里要注意:

错误处理: 必须在回调中检测错误,而不是用 try-catch 块。

没有返回值: 异步函数不返回值,但是值将被传递给回调。

下面我们对这个文件做点修改,看看它实际上是如何工作的:

const fs = require("fs")

console.log("start reading a file...")

fs.readFile("file.md", "utf-8", function (err, content) {  
  if (err) {
    console.log("error happened during reading the file")
    return console.log(err)
  }

  console.log(content)
})

console.log("end of the file")  

这段脚本的输出将是:

start reading a file...  
end of the file  
error happened during reading the file  

正如你所见,一旦我们开始读文件,执行继续,应用程序打印出 end of the file。一旦文件读取完成,我们的回调就只被调用一次。这怎么可能呢?迎接事件循环。

事件循环

事件循环是 Node.js / JavaScript 的核心 - 它负责安排异步操作。

在深入了解之前,要确保理解什么是事件驱动的编程。

事件驱动的编程是一种编程范式,在这种范式中程序流程是由事件决定的,比如用户行为(鼠标点击、按键)、传感器输出或者其它程序/线程的消息。

实际上,它意味着应用程序按照事件行事。

并且,我们在第一章已经学过,从开发者的观点看,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) {  
    // 现在结果是是一个每个文件的状态数组
})
Promises

Promise 对象用于延迟及异步计算。一个 Promise 代表还没有完成,但是未来会执行的操作。

在实践中,前面的示例可以重写为如下:

function stats (file) {  
  return new Promise((resolve, reject) => {
    fs.stat(file, (err, data) => {
      if (err) {
        return reject (err)
      }
      resolve(data)
    })
  })
}

Promise.all([  
  stats("file1"),
  stats("file2"),
  stats("file3")
])
.then((data) => console.log(data))
.catch((err) => console.log(err))

当然,如果使用一个有 Promise 接口的方法,那么 Promise 示例的行数也会少很多。

下一步:第一个 Node.js 服务器

下一章将会学习如何启动第一个 Node.js HTTP 服务器。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/84981.html

相关文章

  • Node Hero】1. 开始使用 Node.js

    摘要:使用一个事件驱动的非阻塞式的模型,让它轻量而高效。也就是说提供了用编写服务器的可能性,这种服务器具有令人难以置信的性能。正如官方声明所说是一个使用与浏览器相同引擎的运行时。这意味着有两个发布版本稳定版和试验版。 本文转载自:众成翻译译者:网络埋伏纪事链接:http://www.zcfy.cc/article/1748原文:https://blog.risingstack.com/nod...

    hqman 评论0 收藏0
  • Node Hero】2. 使用 NPM

    摘要:网站和使用同样的注册库来显示模块以及查找模块。使用在上一章开始使用中,当创建文件时,已经遇到了。此外,全局命名空间只包含公共模块。通过引入作用域包来解决此问题。下一步异步编程下一章学习使用回调和实现异步编程的原理。 本文转载自:众成翻译译者:网络埋伏纪事链接:http://www.zcfy.cc/article/1749原文:https://blog.risingstack.com/n...

    CarterLi 评论0 收藏0
  • 【响应式编程的思维艺术】 (5)Angular中Rxjs的应用示例

    摘要:本文是响应式编程第四章构建完整的应用程序这篇文章的学习笔记。涉及的运算符每隔指定时间将流中的数据以数组形式推送出去。中提供了一种叫做异步管道的模板语法,可以直接在的微语法中使用可观测对象示例五一点建议一定要好好读官方文档。 本文是【Rxjs 响应式编程-第四章 构建完整的Web应用程序】这篇文章的学习笔记。示例代码托管在:http://www.github.com/dashnoword...

    shenhualong 评论0 收藏0
  • Node Hero】7. Node.js 项目结构

    摘要:本教程会学习如何正确组织一个项目的结构,从而在应用程序开始增长时避免混乱。项目结构的五个基本规则组织项目有不少可能的方式并且每种已知的方式都有其兴衰。过去在,我们有机会创建各种规模的高效应用程序,也获得了大量关于项目结构注意事项的见解。 本文转载自:众成翻译译者:网络埋伏纪事链接:http://www.zcfy.cc/article/1756原文:https://blog.rising...

    张红新 评论0 收藏0
  • JavaScript设计模式

    摘要:可能因为先入为主,在编程之中,往往不由自主地以的逻辑编程思路设计模式进行开发。这是原型模式很重要的一条原则。关于闭包与内存泄露的问题,请移步原型模式闭包与高阶函数应该可以说是设计模式的基础要领吧。在下一章,再分享一下的几种常用设计模式。 前 在学习使用Javascript之前,我的程序猿生涯里面仅有接触的编程语言是C#跟Java——忽略当年在大学补考了N次的C与VB。 从静态编程语言,...

    keke 评论0 收藏0

发表评论

0条评论

kevin

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<