(82)Wangdao.com第十六天1017__ JavaScript 异步操作

异步操作

  • 单线程模型

    • 指的是,JavaScript 只在一个线程上运行
    • 也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待
      • 注意,JavaScript 只在一个线程上运行,不代表 JavaScript 引擎只有一个线程。
    • 事实上,JavaScript 引擎有多个线程,单个脚本只能在一个线程上运行(称为主线程),其他线程都是在后台配合
    • JavaScript 之所以采用单线程,而不是多线程,跟历史有关系。
      • JavaScript 从诞生起就是单线程,原因是不想让浏览器变得太复杂,
      • 因为多线程需要共享资源、且有可能修改彼此的运行结果,对于一种网页脚本语言来说,这就太复杂了。
      • 好处:
        • 实现起来比较简单,执行环境相对单纯
        • Node 可以用很少的资源,应付大流量访问的原因
      • 坏处:
        • 只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。
        • 常见的浏览器无响应(假死),往往就是因为某一段 JavaScript 代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行
      • JavaScript 语言本身并不慢,慢的是读写外部数据,比如等待 Ajax 请求返回结果。这个时候,如果对方服务器迟迟没有响应,或者网络不通畅,就会导致脚本的长时间停滞。
      • JavaScript 语言的设计者意识到,这时 CPU 完全可以不管 IO 操作,挂起处于等待中的任务,先运行排在后面的任务。等到 IO 操作返回了结果,再回过头,把挂起的任务继续执行下去。这种机制就是 JavaScript 内部采用的 “事件循环”机制(Event Loop)
      • 为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。所以,这个新标准并没有改变 JavaScript 单线程的本质。
    • 同步任务和异步任务

程序里面所有的任务,可以分成两类:同步任务(synchronous)异步任务(asynchronous)

      • 同步任务

        • 是那些没有被引擎挂起、在主线程上排队执行的任务。
        • 只有前一个任务执行完毕,才能执行后一个任务。
      • 异步任务

        • 是那些被引擎放在一边,不进入主线程、而进入任务队列的任务。
        • 只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。
        • 排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有”堵塞“效应。
        • 举例来说
            • Ajax 操作可以当作同步任务处理,也可以当作异步任务处理,由开发者决定。

              • 如果是同步任务,主线程就等着 Ajax 操作返回结果,再往下执行;
              • 如果是异步任务,主线程在发出 Ajax 请求以后,就直接往下执行,等到 Ajax 操作有了结果,主线程再执行对应的回调函数
    • 任务队列和时间循环

JavaScript 运行时,除了一个正在运行的主线程,引擎还提供一个任务队列(task queue),

里面是各种需要当前程序处理的异步任务。(实际上,根据异步任务的类型,存在多个任务队列。为了方便理解,这里假设只存在一个队列。)

  • 首先,主线程会去执行所有的同步任务。等到同步任务全部执行完,就会去看任务队列里面的异步任务。
  • 如果满足条件,那么异步任务就重新进入主线程开始执行,这时它就变成同步任务了。
  • 等到执行完,下一个异步任务再进入主线程开始执行。一旦任务队列清空,程序就结束执行。
  • 异步任务的写法通常是回调函数。

    • 一旦异步任务重新进入主线程,就会执行对应的回调函数。
    • 如果一个异步任务没有回调函数,就不会进入任务队列,也就是说,不会重新进入主线程,因为没有用回调函数指定下一步的操作。
  • JavaScript 引擎怎么知道异步任务有没有结果,能不能进入主线程呢?

    • 答案就是引擎在不停地检查,一遍又一遍,只要同步任务执行完了,引擎就会去检查那些挂起来的异步任务,是不是可以进入主线程了
    • 这种循环检查的机制,就叫做事件循环(Event Loop)。
    • 维基百科的定义是:“事件循环是一个程序结构,用于等待和发送消息和事件(a programming construct that waits for and dispatches events or messages in a program)”。
    • 异步操作的模式
      • 回调函数

        • 是异步操作最基本的方法

          • 下面是两个函数f1f2,编程的意图是f2必须等到f1执行完成,才能执行

            function f1() {
                // ...
            }
            
            function f2() {
                // ...
            }
            
            f1();
            f2();

            上面代码的问题在于,如果f1是异步操作,f2会立即执行,不会等到f1结束再执行

            • 这时,可以考虑改写f1,把f2写成f1的回调函数

              function f1(callback) {
                  // ...
                  callback();
              }
              
              function f2() {
                  // ...
              }
              
              f1(f2);
        • 回调函数的优点:

          • 简单、容易理解和实现
        • 回调函数的缺点:
          • 不利于代码的阅读和维护,
          • 各个部分之间高度耦合(coupling),使得程序结构混乱、流程难以追踪(尤其是多个回调函数嵌套的情况),
          • 而且每个任务只能指定一个回调函数
      • 事件监听
        • 采用事件驱动模式。
        • 异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。
        • 以 f1 和 f2 为例。首先,为 f1 绑定一个事件(这里采用的 jQuery 的写法)
          • f1.on(‘done‘, f2);    // 当 f1 发生 done 事件,就执行 f2

            f1进行改写:

            • function f1() {
                  setTimeout(function () {
                      // ...
                      f1.trigger(‘done‘);    // 表示,执行完成后,立即触发 done 事件,从而开始执行f2
                  }, 1000);
              }
        • 优点:
          • 比较容易理解,可以绑定多个事件,
          • 每个事件可以指定多个回调函数,
          • 而且可以”去耦合“(decoupling),有利于实现模块化
        • 缺点:
          • 整个程序都要变成事件驱动型,运行流程会变得很不清晰。
          • 阅读代码的时候,很难看出主流程。
      • ”发布/订阅模式”(publish-subscribe pattern),又称“观察者模式”(observer pattern)
        • 事件完全可以理解成”信号“,如果存在一个”信号中心“,
        • 某个任务执行完成,就向信号中心 ”发布“(publish)一个信号,
        • 其他任务可以向信号中心”订阅“(subscribe)这个信号,从而知道什么时候自己可以开始执行。
        • 可以用多种方式实现这个模式

          • 采用的是 Ben Alman 的 Tiny Pub/Sub,这是 jQuery 的一个插件
            • 首先,f2 向信号中心 jQuery 订阅 done 信号

              • jQuery.subscribe(‘done‘, f2);
            • 然后,f1 进行如下改写
              • function f1() {
                    setTimeout(function () {
                        // ...
                        jQuery.publish(‘done‘);
                    }, 1000);
                }

                f1 执行完成后,向信号中心 jQuery 发布 done 信号,从而引发 f2 的执行

            • f2 完成执行后,可以取消订阅(unsubscribe)
              • jQuery.unsubscribe(‘done‘, f2);
            • 性质与“事件监听”类似,但是明显优于后者。
              • 因为可以通过查看“消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行
    • 异步操作的流程控制

      • 如果有多个异步操作,就存在一个流程控制的问题:如何确定异步操作执行的顺序,以及如何保证遵守这种顺序
      • 串行执行:
        • 我们可以编写一个流程控制函数,让它来控制异步任务,一个任务完成以后,再执行另一个。

          var items = [ 1, 2, 3, 4, 5, 6 ];
          var results = [];
          
          function async(arg, callback) {
              console.log(‘参数为 ‘ + arg +‘ , 1秒后返回结果‘);
              setTimeout(function () { callback(arg * 2); }, 1000);
          }
          
          function final(value) {
              console.log(‘完成: ‘, value);
          }
          
          function series(item) {
              if(item) {
                  async( item, function(result) {
                      results.push(result);
                      return series(items.shift());
                  });
              } else {
                  return final(results[results.length - 1]);
              }
          }
          
          series(items.shift());
        • 函数 series() 就是串行函数,它会依次执行异步任务,所有任务都完成后,才会执行final函数。
        • items[] 数组保存每一个异步任务的参数,results[] 数组保存每一个异步任务的运行结果。
        • 注意,上面的写法需要六秒,才能完成整个脚本。
      • 并行执行

        • 流程控制函数也可以并行执行,即所有异步任务同时执行,等到全部完成以后,才执行 final 函数

          • var items = [ 1, 2, 3, 4, 5, 6 ];
            var results = [];
            
            function async(arg, callback) {
                console.log(‘参数为 ‘ + arg +‘ , 1秒后返回结果‘);
                setTimeout(function () { callback(arg * 2); }, 1000);
            }
            
            function final(value) {
                console.log(‘完成: ‘, value);
            }
            
            items.forEach(function(item) {
                async(item, function(result){
                    results.push(result);
                    if(results.length === items.length) {
                        final(results[results.length - 1]);
                    }
                })
            });
    • 1
    • 1
    • 1
    • 1
    • 1
    • 1
    • 1
    • 1
    • 1
    • 1
    • 1
    • 1
    • 1
    • 1
    • 1
    • 2

原文地址:https://www.cnblogs.com/tianxiaxuange/p/9807263.html

时间: 2024-10-06 20:43:49

(82)Wangdao.com第十六天1017__ JavaScript 异步操作的相关文章

__x__(81)1017第十六天__ JavaScript 严格模式

严格模式 除了正常的运行模式,JavaScript 还有第二种运行模式:严格模式(strict mode).顾名思义,这种模式采用更加严格的 JavaScript 语法 同样的代码,在正常模式和严格模式中,可能会有不一样的运行结果. 一些在正常模式下可以运行的语句,在严格模式下将不能运行. 设计目的 早期的 JavaScript 语言有很多设计不合理的地方,但是为了兼容以前的代码,又不能改变老的语法, 只能不断添加新的语法,引导程序员使用新语法 严格模式是从 ES5 进入标准的,主要目的有以下几

第十五天和十六天学习笔记

第十五天学习笔记: 主要学习了HTML5: 1 <!DOCTYPE html> <!--文档类型声明--> 2 <html> 3 <head> 4 <title>学习HTML5</title> 5 <meta charset = "UTF-8"> 6 </head> 7 <body> 8 <p>学习html5</p> 9 </body> 10

安卓第十六天笔记-音频与视频播放

安卓第十六天笔记-音频与视频播放 音频与视频播放 1.音频 播放应用资源中的音乐 应用中的音乐一般放在res/raw目录下 /** * 播放应用资源中的音乐 * * @param v */ public void player1(View v) { // 设置播放数据源 MediaPlayer mediaPlayer = MediaPlayer.create(this, R.raw.gm); // 不需要准备,create,创建完成直接可以使用播放 mediaPlayer.start(); }

Training的第十六天

今天知道了很多也学会了很多.从哲学层面和行业层面粗略了解了一下IT这个大的体系.哲学层面从价值的角度来阐释了IT的发展对人类社会产生的影响,在以后的编写程序中,要形成一个价值观:编写的这个程序怎样才能产生最大的价值.这就需要我掌握更加多的知识,例如传输协议,用户界面设计,人机交互,代码的维护等等.罗马不是一天建成的,所以我也需要慢慢学习其他的知识并在其中发现自己的长处.因为一个好的程序,需要考虑的东西太多了,人需要知识广泛,但是也需要精通一门技术.一个好的产品在它的背后都有一个强大的团队. 今后

javaSE第十六天

第十六天????140 1:List的子类(掌握)????140 (1)List的子类特点????140 (2)ArrayList????141 A:没有特有功能需要学习????141 B:案例????141 a:ArrayList存储字符串并遍历????141 b:ArrayList存储自定义对象并遍历????142 (3)Vector????143 A:有特有功能????143 B:案例????143 a:Vector存储字符串并遍历(与ArrayList案例代码相似)????143 b:V

第二十六天 蛰伏的Hibernate遇到春日的暖阳 —Spring MVC 集成Hibernate使用(一)

6月7日,晴."纷纷红紫已成尘,布谷声中夏令新.夹路桑麻行不尽,始知身是太平人."        Hibernate和Spring的香艳相逢,不仅是Bean和Bean之间电光火石般的碰撞,也是XML和XML之间的萍水聚首.两种奇妙的架构,携手闯荡江湖,不仅塑造了依赖注入淋漓酣畅的快感,也让J2EE蹿红大半个地球. 第二十六天 蛰伏的Hibernate遇到春日的暖阳 -Spring MVC 集成Hibernate使用(一)

javaSE第二十六天

第二十六天????414 1:网络编程(理解)????414 (1)网络编程:用Java语言实现计算机间数据的信息传递和资源共享????414 (2)网络编程模型????414 (3)网络编程的三要素????414 (4)网络编程中主要类介绍:????416 A:InetAddress: 此类表示互联网协议 (IP) 地址.????416 B:????UDP协议下的Socket对象类DatagramSocket????418 1.发送端:????418 2.接收端:????419 C:????T

《Javascript权威指南》学习笔记之十四:JavaScript内建类

前面的几篇博文分别介绍了对象.字符串.数组.日期等内建类,本篇将介绍Boolean/Math/Function/Arguments类 一.使用Boolean类处理逻辑值 Boolean类是JS的一个封装类,可以用于获取Boolean对象的原始数据类型或者字符串表示形式.new Boolean(value)用于创建一个Boolean对象,Boolean(value)它的参数转换成一个原始的布尔值,并且返回这个值.Boolean对象只有两个值:true或者false. value参数可以省略.如果省

UI学习笔记---第十六天XML JSON解析

一.解析的基本概念 从事先规定好的格式中提取数据 解析的前提:提前约定好格式.数据提供方按照格式提供数据,数据方按照格式获取数据 常见解析方式XML解析JSON解析 二.XML:可扩展标记语言 XML数据格式的功能:数据交换 web服务  内容管理  用作配置文件 一对标签称为一个节点  节点可以包含节点   没有子节点叫叶子节点  只有一个根节点  节点可以相互嵌套 三.进行XML解析时使用到的SAX工具 SAX:Simple API for XML  基于事件驱动的解析方式,逐行解析数据.(