美文网首页
js 线程 之 异步编程(1)

js 线程 之 异步编程(1)

作者: WEB_Jorie | 来源:发表于2018-07-06 13:53 被阅读0次

    ----欢迎查看我的博客----

    js 单线程

      相信大家玩 js 很久了,Js语言的执行环境是 “ 单线程 ”。什么是单 线程,就是当一个任务完成之后才会有下一个任务 。 举个栗子,你在奶茶店买奶茶 ,只有一个 奶茶妹妹,客人们都在排队,问你喝什么,你告诉她,她下好单再叫下一个,以此类推,这就是 单线程的同步。再比如到你了,你没选好喝什么,她先问下一位喝什么,等你想好了,再问你喝什么,这就是 单线程的异步。多个奶茶妹妹同事问客人 ,就是多线程。

    js 为什么是单线程的。

      为什么 JavaScript 不能有多个 线程 呢?这样能提高效率啊。办事快啊, 我能撩到好几个奶茶妹妹,但这个和 js 的作用有关 ,当初 js 只是为了服务于 游览器 ,用户交互,所以设计了单 线程 , 如果多 线程 你想有多么麻烦,你操作了 dom 是修改 ,同时另外的线程执行了 删除 dom 这时候游览器怎么选择呢?为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主 线程 控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。所以我们任然 坚持说 js 是单 线程 的。

    异步同步

      既然 js 是 单线程 的,那么按照我们的之前的栗子, 你去买奶茶,你没想好要喝什么,一直占着位置,估计后面的兄弟们对你肯定不爽了,开始骂娘了 ,所以 这时候我们的 奶茶妹 就很机智,然你在后面等等,先问下一个要喝什么。于是,所有任务可以分成两种。

    同步任务(synchronous)

    在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;

    执行过程大概是这样:
    (1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
    (2)一旦"执行栈"中的当前任务执行完毕,就会执行下一个任务,进入执行栈,开始执行。
    (3)主线程不断重复上面的(2)

    异步任务(asynchronous)

      不进入主线程、而进入”任务队列”(task queue)的任务,只有”任务队列”通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
    执行过程大概是这样:
    (1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
    (2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
    (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。开始执行。
    (4)主线程不断重复上面的(3)

    浏览器不是单线程的

    虽然JS运行在浏览器中,是单线程的,每个window一个JS线程,但浏览器不是单线程的,可能有如下线程:

    javascript引擎线程

    这个就是我们常说的单线程

    界面渲染线程

    渲染dom页面的进程

    浏览器事件触发线程

    有可能触发多个事件。例如 keyup ,clickup 等。

    Http请求线程

    你可以发起多个请求,至于最多能发送多少请求,我们后面会提到。

    如果js是单线程的,那么谁去轮询大的Event loop事件队列?答案是浏览器会有单独的线程去处理这个队列。

    js 的 异步编程

      异步编程有这么几种方式:

    ES 6以前:

    • 回调函数
    • 事件监听(事件发布/订阅)

    ES 6:

    • Generator函数(协程coroutine)
    • Promise对象

    ES 7:

    • async和await

    * 回调函数

     //一个定时器
     function timer(time, callback){
         setTimeout(function(){
             callback();
         }, time);
     };
      
     timer(3000, function(){
          console.log(123);
     })
    

       这个最常见,也是我们经常写的。非常简单。一个 setTimeout 然后执行后面的函数。至于 setTimeout 的话 ,我们后面再讲。

    * 事件监听(发布/订阅)

      发布—订阅模式可以广泛应用于异步编程中,这是一种替代传递回调函数的方案。比如,我们可以订阅 ajax 请求的 error 、 succ 等事件。 或者如果想在动画的每一帧完成之后做一些事情,那我们可以订阅一个事件,然后在动画的每一帧完成之后发布这个事件。在异步编程中使用发布—订阅模式,我们就无需过多关注对象在异步运行期间的内部状态,而只需要订阅感兴趣的事件发生点。
      这里不详细说了,因为后面我们会把这种设计模式单独拿出来讲。这里举例一个事件监听,其实是一样的:

    element.addEventListener("click",function(){
        alert("clicked");
    })
    

    * Generator函数

    这个函数我用的很少,所以我打算后面专门也来研究一下这个东西。
    形式上,Generator 函数是一个普通函数,但是有两个特征。

    function关键字与函数名之间有一个星号;

    函数体内部使用yield语句,定义不同的内部状态

    我们来大概看看这个东西

    function* helloWorldGenerator() {
      yield 'hello';
      yield 'world';
      return 'ending';
    }
    var hw = helloWorldGenerator();
    
    hw.next()
    // { value: 'hello', done: false }
    hw.next()
    // { value: 'world', done: false }
    hw.next()
    // { value: 'ending', done: true }
    hw.next()
    // { value: undefined, done: true }
    

    Generator函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。不同的是,调用Generator函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,我们可以通过调用 next 方法,使得指针移向下一个状态。

    * Promise对象

      这个就比较熟了,之前有提到。不太熟悉的童鞋们可以看这篇文章: 你就需要这篇文章,带你搞懂实战的 Promise 对象 ; 后面我们也会将他拿出来,看看我们的 JavaScript 原生如何实现 promise 对象。

    * async和await

      这个也比较熟悉,之前的博客也提到过 ,了解 JavaScript ES7 的 async / await , ; 后面我们也会将他拿出来,看看我们的 JavaScript 原生如何实现 async / await 对象。

    结语

      以上就是我们的异步编程方式,我们后面会慢慢将这些东西拿出来讲解 :

    • setTimeout
    • 发布订阅
    • 原生(es2015)实现 promiss
    • Generator
    • 原生(es2015)实现 async / await

    相关文章

      网友评论

          本文标题:js 线程 之 异步编程(1)

          本文链接:https://www.haomeiwen.com/subject/ornvuftx.html