本系列的例子主要针对node.js环境,但浏览器端的原理应该也是类似的。

本人也是Javascript新手,把自己这段时间学习积累的要点总结下来,希望可以对同样在学习Javascript/node.js的同学有一些参考价值。尽量用通俗的语言帮助大家理解,如果有描述或理解不准确的地方欢迎大家指正,交流。另外本文假定你已经对javascript的语法和异步有一些基本的概念。
 
本系列会按一般学习异步编程的顺序,首先介绍一下异步的原理,然后介绍各种异步编程的方法,从回调函数开始,然后慢慢进入Promise和Generator等对异步编程体验进行改进的技术。期间也会大概提一下事件监听方式的异步调用,但是因为太多的事件监听会让程序流程变的不清晰,所以不太推荐(其实事件模式本质上也是回调函数)。Promise和Generator才是到目前为止(ES6),最好的异步编程方式。后面也会分享一下自己所理解的这两种方式的对比(网上也有很多争论)。ES7提出了更好的改进方案,希望很快可以抽出时间研究一下,这是后话:)
 
好,进入本节的正文。
 
什么是异步?同步异步与阻塞非阻塞有什么关系?
 
node.js的“一切皆异步”的思想很有创意,目的是可以让开发者轻松编写高性能的web服务端,而不会“不小心”就用同步api阻塞了服务器从而影响性能。其他的语言比如php, python, java等基于同步的语言,虽然也有异步api,但毕竟编程人员的“思想上是同步的”,有时候不可避免的会写出阻塞的代码,node.js的目标是造就“思想上是完全异步”的编程人员和编程语言:)
 
异步跟同步最大的不同就是异步api或函数被“调用”后不会等它运行结束再执行它后面的代码,而是调用之后直接往下执行,异步函数的“执行”实际上是放在“其他地方”,待“执行”完成后再把结果通过回调函数来进行进一步的使用或处理(所以异步函数书写的时候不要用"return"来返回值哦,必须通过回调函数来返回值)。这里为什么强调“调用”和“执行”两个词呢?就是为了更好的理解异步的过程。
 
打个比喻,你的领导要做个电子报表,笨领导的做法是,在自己电脑上把该统计的统计了,该算的算了,最后生成一张报表,然后用这个报表做接下去的工作;聪明的领导,会发个邮件给下属,把报表的要求写清楚,下属在自己的电脑上把报表做完后,发个邮件把报表交回给领导。在下属做报表的时候,领导可以在自己电脑上继续做其他事情,比如玩游戏、看视频等等(你懂的)。
 
在上面这个例子中,领导是编程者(你),领导的电脑是当前线程,下属的电脑是另一个线程(如果有多个下属就相当于有个线程池)。做报表这件工作是个异步函数,发邮件给下属相当于调用这个函数,下属电脑上做报表相当于在另一个线程异步执行这个函数,执行完了发邮件把报表发回给领导相当于调用回调函数,领导就可以使用这个报表接着做下面的工作(相当于回掉函数里面的代码)。下属做报表的时候领导完全不用管而是可以继续干其他事情。
 
通过这个例子可以清楚的看到,领导只能在他自己的电脑(用户线程)上工作,异步的函数都是在下属的电脑上(异步线程)做的。这一点在很多文章当中并没有讲的很清楚,所以容易造成困扰,因为很多人只是一味的强调javascript是单线程的,但单线程怎么能实现异步呢?就并没有讲清楚。其实所谓的单线程是指用户线程是单线程,而另外还有一个或多个线程处理异步代码的执行。
 
接下来再说说阻塞的问题。很多文章里面在讲解的时候同步异步阻塞非阻塞混为一团,新手很难理解,最容易产生的误解就是同步=阻塞,异步=非阻塞。其实阻塞非阻塞跟同步异步没有任何关系。简单讲,阻塞就是一个api或者函数运行时间过长,而独占cpu导致其他代码不能运行,那么多长时间算阻塞呢?相信这只是一个相对的概念,只要明显影响到程序的性能和用户体验,就算阻塞吧。那跟同步异步是什么关系呢?阻塞的代码如果同步执行就会阻塞到自己后面代码的运行,所以自然而然的就想到异步来执行阻塞的代码,然而异步真能解决阻塞问题吗?下面继续讲。
 
node.js里面的异步
 
大家知道Javascript的基本语法跟其他编程语言大同小异,最大的不同就是把异步摆在首位,特别是node.js更是大部分api都是异步的,小量同步api。这与其他大部分语言刚好相反,也给习惯于同步编程思维的同学造成了很大的困扰,就是常说的转不过弯来,习惯性的认为代码是按顺序一行一行的往下执行。
 
上面介绍了异步非阻塞的概念,那么具体到node.js是怎么实现的呢?这里又会涉及到I/O的概念,具体不讲I/O的细节(操作系统原理都会讲),关键就一点,I/O操作通常比较耗时但不会独占CPU,典型的I/O比如文件读写,远程数据库读写,网络请求等。 先讲耗时,如果用同步API来进行I/O操作,在返回结果之前就只能等待,所以最好的办法上面已经讲过,就是进行异步操作。接着说一下不会霸占CPU的好处。在node.js进程里面,有一个用户线程(javascript所宣称的单线程)和一个异步线程池(用户无法直接访问), 如果跑在异步线程上的代码是阻塞的,那么这种异步根本就起不到消除阻塞的作用,为什么?原因就是阻塞代码会霸占cpu,导致本进程所有代码都等待不管是哪个线程。但是,,,刚刚讲的node.js里面的I/O API都是不会霸占CPU的,所以是非阻塞的,就不会出现这个问题。这就是node.js的最引以为傲的特性之一:异步非阻塞I/O.
 
上面只是强调异步非阻塞,那么对于真正的阻塞代码,node.js怎么办呢?不好意思。。。单个node.js进程真无能为力,跟同步编程语言相比没什么优势,只能用传统的方式,多进程,多开几个node.js进程,甚至多开几个服务器。任何语言都有它的强项和弱项,node.js的强项就是它本来的设计初衷:让开发者能够轻松的编写高性能的web服务器(进行的最多的就是网络和数据库的I/O操作),而不是做大量的CPU密集型的运算(不过我赶脚,就算要做很多阻塞操作,用多进程和多服务器又有何不可?node.js对此提供了足够的支持)。这样算是回答了上一小节最后的问题了吧:)
 
要理解javascript异步编程和其他语言同步编程的区别,可以从一个最简单的例子开始。在同步为主的语言中,如果需要等待10秒钟,通常是类似sleep 10之类的语句,在10秒之内整个进程挂起,也就是阻塞10秒。但javascript不是这样,它是使用setTimeout函数,从字面意思就已经可以看出区别了, 设一个timeout的时间, 在这段时间内cpu可以继续运行其他代码, 等10秒时间到了, 就用回调函数的形式来做应该10秒之后才做的事情。
 
到此,这一节异步编程的原理大概就概括了一下,在进一步深入学习异步编程之前一定要理解的概念。以上都是自己的理解,错误或不准确的地方还望不吝赐教:)
 
下一节会介绍最基础的javascript的异步编程方式:回调函数,所有其他方式都是基于它的改进。
 
附注:也许是我读得书少:) 这么久以来没有发现网上有对异步编程讲解的很透彻的文章,在自己学习过的资料当中,朴灵的《深入浅出node.js》是讲解的最深入透彻的,强烈推荐。
 

Javascript异步编程之一异步原理的更多相关文章

  1. C#复习笔记(5)--C#5:简化的异步编程(异步编程的基础知识)

    异步编程的基础知识 C#5推出的async和await关键字使异步编程从表面上来说变得简单了许多,我们只需要了解不多的知识就可以编写出有效的异步代码. 在介绍async和await之前,先介绍一些基础 ...

  2. .NET “底层”异步编程模式——异步编程模型(Asynchronous Programming Model,APM)

    本文内容 异步编程类型 异步编程模型(APM) 参考资料 首先澄清,异步编程模式(Asynchronous Programming Patterns)与异步编程模型(Asynchronous Prog ...

  3. C#复习笔记(5)--C#5:简化的异步编程(异步编程的深入分析)

    首先,阐明一下标题的这个“深入分析”起得很惭愧,但是又不知道该起什么名字,这个系列也主要是做一些复习的笔记,供自己以后查阅,如果能够帮助到别人,那自然是再好不过了. 然后,我想说的是异步方法的状态机真 ...

  4. 简述异步编程&Promise&异步函数

    前言:文章由本人在学习之余总结巩固思路,不足之前还请指出. 一.异步编程 首先我们先简单来回顾一下同步API和异步API的概念 1.同步API:只有当前的API执行完成之前,才会执行下一个API 例: ...

  5. 【C#TAP 异步编程】异步接口 OOP

    在我们深入研究"异步OOP"之前,让我们解决一个相当常见的问题:如何处理异步方法的继承?那么"异步接口"呢? 幸运的是,它确实可以很好地与继承(和接口)一起使用 ...

  6. Javascript异步编程之二回调函数

    上一节讲异步原理的时候基本上把回掉函数也捎带讲了一些,这节主要举几个例子来具体化一下.在开始之前,首先要明白一件事,在javascript里函数可以作为参数进行传递,这里涉及到高阶函数的概念,大家可以 ...

  7. javascript异步编程的前世今生,从onclick到await/async

    javascript与异步编程 为了避免资源管理等复杂性的问题, javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是为 ...

  8. 5分种让你了解javascript异步编程的前世今生,从onclick到await/async

      javascript与异步编程 为了避免资源管理等复杂性的问题,javascript被设计为单线程的语言,即使有了html5 worker,也不能直接访问dom. javascript 设计之初是 ...

  9. JavaScript将最终获得正确的异步编程

    JavaScript将最终获得正确的异步编程 包括该提案异步 在ECMAScript中的功能已经达到第四阶段; 这意味着它将在2017年发布的标准.但是这对JavaScript开发者意味着什么? 有很 ...

随机推荐

  1. Nuget私有服务搭建实战

    最近更新了Nuget私有服务器的版本,之前是2.8.5,现在是2.11.3. Nuget服务器的搭建,这里有篇很详细的文章,跟着弄就好了: https://docs.microsoft.com/en- ...

  2. #Java学习之路——基础阶段(第四篇)

    我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...

  3. [Swift]LeetCode199. 二叉树的右视图 | Binary Tree Right Side View

    Given a binary tree, imagine yourself standing on the right side of it, return the values of the nod ...

  4. [Swift]LeetCode440. 字典序的第K小数字 | K-th Smallest in Lexicographical Order

    Given integers n and k, find the lexicographically k-th smallest integer in the range from 1 to n. N ...

  5. [Swift]LeetCode944. 删除列以使之有序 | Delete Columns to Make Sorted

    We are given an array A of N lowercase letter strings, all of the same length. Now, we may choose an ...

  6. 一个 Vue & Node 的全栈小项目

    约学 - 可以寻找一起自习的小伙伴的Web APP 一个基于 Vue & Node 的移动端全栈小项目 在线演示(请使用移动端查看效果) 源码地址: https://github.com/G- ...

  7. SecureCRT 5.2.2版本下载和注册码

    Name: Apollo Interactive Company: Apollo Interactive Serial Number: 03-50-023223License Key: ABMVSR ...

  8. 【Docker】(5)---springCloud注册中心打包Docker镜像

    [Docker](5)---springCloud注册中心打包Docker镜像 上一篇文章讲了将镜像推送到远处私有仓库,然后再从私有仓库拉取该镜像的过程.而这里的镜像是直接从Docker拉取的. 所以 ...

  9. asp.net core系列 38 WebAPI 返回类型与响应格式--必备

    一.返回类型 ASP.NET Core 提供以下 Web API Action方法返回类型选项,以及说明每种返回类型的最佳适用情况: (1) 固定类型 (2) IActionResult (3) Ac ...

  10. TypeScript: type alias 与 interface

    官方文档中有关于两者对比的信息,隐藏在 TypeScript Handbook 中,见 Interfaces vs. Type Aliases 部分. 但因为这一部分很久没更新了,所以其中描述的内容不 ...