Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javascript异步编程 #8

Open
FanWalker opened this issue Oct 1, 2017 · 0 comments
Open

Javascript异步编程 #8

FanWalker opened this issue Oct 1, 2017 · 0 comments

Comments

@FanWalker
Copy link
Owner

FanWalker commented Oct 1, 2017

异步变成和JavaScript运行机制是息息相关的。

JavaScript 运行机制 和 Event Loop

为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM

定时器setTimeout

console.log(1);
setTimeout(function(){console.log(2);},1000);
console.log(3);

上面代码的执行结果是1,3,2,因为setTimeout()将第二行推迟到1000毫秒之后执行;如果时间改成0,即setTimeout的第二个参数为0,执行结果同样也是1,3,2,它的意思是当前代码执行完成(执行栈清空)后,立即执行指定的回调函数,因为回调函数是放在任务队列(task queue)中的,执行栈中的同步任务比任务队列中的任务优先执行

任务队列(task queue)

JavaScript的单线程意味着,所有任务需要排列,前一个任务完成后才会执行下一个任务,如果前一个任务耗时比较长,下一个任务不得不等着。

一般可以先挂起耗时比较长的任务,先让后面的任务先执行,等之前的任务有了结果,再继续执行之前的任务。

任务可分为:
同步任务:在主线程中排队等待执行的任务
异步任务:异步任务则不进入主线程,而是进入任务队列。只有任务队列通知了主线程某异步任务可以执行了,该任务才会进入主线程被执行。

耗时比较长的任务一般要异步执行。
异步任务通常可以分为两大类:I/O 函数(AJAX、readFile等)和计时函数(setTimeout、setInterval)

其实所谓的『回调函数』,就是那些被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。

异步执行的运行机制
(1)所有的同步任务在主线程上运行,形成执行栈
(2)除了主线程,还有一个“任务队列”,只要异步任务有了结果,就会在“任务队列”中放置事件
(3)当“执行栈”中的任务执行完毕,系统就会读取“任务队列”,里面的事件会结束等待状态,进入执行栈,开始执行
(4)主线程重复执行第三步

"任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列",等待主线程读取。

事件循环 Event Loop

主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环):

上图中,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。

二、发布/订阅

我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)。

这个模式有多种实现,下面采用的是Ben Alman的Tiny Pub/Sub,这是jQuery的一个插件。

首先,f2向"信号中心"jQuery订阅"done"信号。

 jQuery.subscribe("done", f2);

然后,f1如下:

function f1(){
 setTimeout(function () {
  // f1的任务代码
  jQuery.publish("done");
 }, 1000);
}

jQuery.publish("done")的意思是,f1执行完成后,向"信号中心"jQuery发布"done"信号,从而引发f2的执行。
此外,f2完成执行后,也可以取消订阅(unsubscribe)。
jQuery.unsubscribe("done", f2);

三、Promise对象(deferred 对象)

Promise 是一个管理事务的对象。
Promise有三个状态:
Pending(进行中)、Resolved(已完成)、Rejected(已失败)。
只有异步操作的结果才决定当前是哪一种状态。

简单说,它的思想是,每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指定回调函数。比如,f1的回调函数f2,可以写成:
f1().then(f2);

f1要进行如下改写(这里使用的是jQuery的实现):

  function f1(){
    var dfd = $.Deferred();
    setTimeout(function () {
      // f1的任务代码
      dfd.resolve();
    }, 500);
    return dfd.promise;
  }

这样可以指定多个回调函数:
f1().then(f2).then(f3);

学习参考:

Javascript异步编程的4种方法
JavaScript 异步编程学习笔记
Promise 对象
JavaScript 运行机制详解:再谈Event Loop

@FanWalker FanWalker changed the title Javascript实现异步编程 Javascript异步编程 Oct 2, 2017
@FanWalker FanWalker changed the title Javascript异步编程 Javascript运行机制 Oct 2, 2017
@FanWalker FanWalker changed the title Javascript运行机制 Javascript异步编程 Oct 2, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant