js中的同步异步

“同步”  “异步” 这个词平时见到的很频繁,感觉一直出现在平时的生活中,但是没怎么用过,写代码的时候也很少往这上面去考虑。

第一次看到 “同步” “异步” 是寒假写小程序的时候, 有一个方法当时特意用的同步(小程序很多方法都分为同步和异步两种 可能因为比较敏感把)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  onCollectionTap: function (event) {
 
    var postsCollected = wx.getStorageSync('posts_collected');
    var postCollected = postsCollected[this.data.currentPostId];
    postCollected = !postCollected;
    //取反操作 让收藏变成未收藏 为收藏变成收藏
    postsCollected[this.data.currentPostId] = postCollected;
    //更新文章 是否 的缓存值
 
    // wx.showToast({
    //   title: postCollected?'收藏成功':"取消成功",
    //   duration: 900,
    //   icon:"success"
    this.showToast(postsCollected, postCollected);
  },

 

 这个是从本地获取缓存数据,看这篇文章是否被我收藏了(因为没有后台记录 是动态的绑定数据 所以要这样做去避免刷新之后之前的收藏就没了)  那么我去获取那个我用来标记是否之前收藏过的变量时,就得是一个同步的方法。  那么这个时候就要思考  同步和异步是什么, 什么时候应该用哪一种。

就这个问题 我当时问过一个学长,他当时给我解释的意思,大概就是看 这件事情 重不重要 对于我进程影响大不大 去考虑用同步好 还是异步好  。 举个看的很多的例子。  关于点单这个问题

平时买奶茶,可能先去点个单,跟人家说 我要一杯观音铁奶,好,然后这个时候你想去旁边的文具店逛一逛文具,看看有啥书想看的想买的。因为我已经买单了,我可以先去做别的事情了 那么我不需要坐在那里等待,因为我没必要一直等着人家做好 然后拿着奶茶去逛文具店,这对我不是那么重要,我完全可以先做别的事情 过会回来拿。所以这就是异步了,我已经买单,那么我不打算等待,老板做奶茶这件事情我不是很关心,所以我可以同时做一些别的事情,似乎这个时候两件事情是一起做的。(后面会提出我的一点疑问) 那接着我在文具店了,诶,这个时候我我想买一本柯南,但是书架上最新的柯南卖完了,我不知道我是否看过最新本的,我必须等着老板帮我找库存,然后拿出来,我再确定我是否要买(这是假设我一定要等着这件事情,我很关心 很看重,跟买奶茶不一样)那么这个过程就是同步的。

 

 

Javascript语言是一门单线程的语言

 单线程和异步确实不能同时成为一个语言的特性。js选择了成为单线程的语言,所以它本身不可能是异步的,

但是你想啊,浏览器只分配给js一个主线程,用来执行那些函数,一次只能执行一个,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的(我之前也说过,有的时候 前端没写好,,其实是可以当黑客的 把人家电脑卡死),比如网络请求,定时器(定时器一定要记得清啊! 我之前写像素鸟的时候  那个管子不停的生成 虽然会清理 但是我把页面就放那里 不管它 可能 过一会 它就自己积累了一堆的管子在那里  特别吓人 )和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。下图说明了浏览器的主要线程。那js的宿主环境(浏览器)就很显而易见的可以看出来是多线程了把,于是 为了解决js的这些严重问题  ,宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性 ,


这就是js的神奇地方  个人觉得计算机有意思就是因为有很多神奇的机智的地方

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。于是就有一个概念——任务队列。如果排队是因为计算量大,CPU忙不过来,倒也算了,但是很多时候CPU是闲着的,因为IO设备(输入输出设备)很慢(比如Ajax操作从网络读取数据),不得不等着结果出来,再往下执行。于是JavaScript语言的设计者意识到,这时主线程完全可以不管IO设备,挂起处于等待中的任务,先运行排在后面的任务。等到IO设备返回了结果,再回过头,把挂起的任务继续执行下去。

于是,所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行。

具体来说,异步运行机制如下:

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

 

这个时候我看到第二,三条我就在想 按它文字逻辑,就成了我把那些函数放在任务队列,等我有空了再去执行那个函数,如果我一开始就决定最后做那件事情 就变得跟同步没啥区别了

开始各种查资料 先贴一些别人博客的描述

 

异步和多线程有什么区别?其实,异步是目的,而多线程是实现这个目的的方法。异步是说,A发起一个操作后(一般都是比较耗时的操作,如果不耗时的操作就没有必要异步了),可以继续自顾自的处理它自己的事儿,不用干等着这个耗时操作返回。.Net中的这种异步编程模型,就简化了多线程编程,我们甚至都不用去关心Thread类,就可以做一个异步操作出来。

 

异步操作的本质

  所有的程序最终都会由计算机硬件来执行,所以为了更好的理解异步操作的本质,我们有必要了解一下它的硬件基础。 熟悉电脑硬件的朋友肯定对DMA这个词不陌生,硬盘、光驱的技术规格中都有明确DMA的模式指标,其实网卡、声卡、显卡也是有DMA功能的。DMA就是直 接内存访问的意思,也就是说,拥有DMA功能的硬件在和内存进行数据交换的时候可以不消耗CPU资源。只要CPU在发起数据传输时发送一个指令,硬件就开 始自己和内存交换数据,在传输完成之后硬件会触发一个中断来通知操作完成。这些无须消耗CPU时间的I/O操作正是异步操作的硬件基础。所以即使在DOS 这样的单进程(而且无线程概念)系统中也同样可以发起异步的DMA操作。

 

异步调用并不是要减少线程的开销, 它的主要目的是让调用方法的主线程不需要同步等待在这个函数调用上, 从而可以让主线程继续执行它下面的代码.与此同时, 系统会通过从ThreadPool中取一个线程来执行,帮助我们将我们要写/读的数据发送到网卡.由于不需要我们等待, 我们等于同时做了两件事情. 这个效果跟自己另外启动一个线程来执行等待方式的写操作是一样的.但是, 异步线程可以利用操作系统/.Net的线程池, 系统可以根据吞吐量动态的管理线程池的大小.

=======================================================================
异步与多线程,从辩证关系上来看,异步和多线程并不时一个同等关系,异步是目的,多线程只是我们实现异步的一个手段.什么是异步:异步是当一个调用请求发送给被调用者,而调用者不用等待其结果的返回.实现异步可以采用多线程技术或则交给另外的进程来处理

 

 

看了这么多之后 我似乎有点懂了 ,再返回去看 前面提到的  IO设备(输入输出设备) 和 CPU    哇就觉得自己好蠢...  差点就想为了这件事情去问斌哥  还好还好 我选择了自己思考 

再来捋一遍,为什么会卡呢  因为 IO太慢了啊  而IO跟cpu没有冲突啊...  我完全可以让它慢慢输入输出  然后我自己先去执行别的  比如我先去逛逛文具店  所以 其实两件事情确实是同时在发生的,但是并没有占着一个线程,所以不叫多线程,本质上依旧是单线程   再看一眼那一大段引用的关于异步操作的本质的文字   拥有DMA功能的硬件在和内存进行数据交换的时候可以不消耗CPU资源  所以一点都不冲突。 【我对计算机的这些东西有一些盲点,,所以也不知道理解的是否正确 】

 

 

 以上是针对于一些数据读取异步过程的一个处理  但是像定时器,感觉好像 应该不是这样 于是去查了一下  还有一个东西叫  Web Worker

Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

Worker 线程一旦新建成功,就会始终运行,不会被主线程上的活动(比如用户点击按钮、提交表单)打断。这样有利于随时响应主线程的通信。但是,这也造成了 Worker 比较耗费资源,不应该过度使用,而且一旦使用完毕,就应该关闭。

Web Worker 有以下几个使用注意点。

(1)同源限制

分配给 Worker 线程运行的脚本文件,必须与主线程的脚本文件同源。

(2)DOM 限制

Worker 线程所在的全局对象,与主线程不一样,无法读取主线程所在网页的 DOM 对象,也无法使用documentwindowparent这些对象。但是,Worker 线程可以navigator对象和location对象。

(3)通信联系

Worker 线程和主线程不在同一个上下文环境,它们不能直接通信,必须通过消息完成。

(4)脚本限制

Worker 线程不能执行alert()方法和confirm()方法,但可以使用 XMLHttpRequest 对象发出 AJAX 请求。

(5)文件限制

Worker 线程无法读取本地文件,即不能打开本机的文件系统(file://),它所加载的脚本,必须来自网络。

 

 

 

 

 

 

 

最后一个环节,再来看几个关于异步的一些题目(更新中...  demo还没完全弄好)

 

 

 

 

 

-------~~~  (  回调函数使用不多,所以等以后详细用过之后再来写博客  )   ~~~-------

 

 

-------~~~   to be continued ~~~-------

posted @ 2018-12-03 19:45  -xw  阅读(3608)  评论(0编辑  收藏  举报