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

我知道的异步——初识 #5

Open
liuduanyang opened this issue Dec 25, 2017 · 0 comments
Open

我知道的异步——初识 #5

liuduanyang opened this issue Dec 25, 2017 · 0 comments

Comments

@liuduanyang
Copy link
Owner

liuduanyang commented Dec 25, 2017

“现在”指的是主线程中代码开始执行时
"将来"指的是一些异步任务完成时

我们所研究的异步机制就是用来处理"现在"到"将来"这一过程

任何时候,只要把一段代码包装成一个函数,并指定它在响应某个事件(如定时器、鼠标点击、Ajax响应等)时执行,你就是在代码中创建了一个将来执行的块,也由此在这个程序中引入了异步机制

事件循环:举例来说,如果javascript程序发出一个Ajax请求,要从服务器获取一些数据,我们需要在回调函数中设置好响应代码,然后javascript引擎会通知宿主环境:“嘿,现在我要暂停执行,你一旦完成网络请求,拿到了数据,就请调用这个函数”,然后浏览器就会设置某个线程监听来自网络的响应,拿到要给你的数据后,就会把回调函数插入到事件循环,等待javascript引擎的调用执行。

事件循环是一个先进先出的队列,循环每一轮称为一个 tick 。如果队列中有等待事件(宿主环境执行完某个异步任务后,就会把相应的回调函数添加到事件循环队列,所说的等待事件指的就是这些回调函数)

javascript引擎时刻准备着,在需要的时候执行相应的代码块(上述回调函数,假设主线程已执行完毕)

注意,并不是发起异步操作的那些代码将你的回调函数放入队列,比如 setTimeout() 并没有把你的回调函数挂在事件循环队列中,它所做的仅仅是设定(发起)一个定时器,当到指定时间后,环境会把你的回调函数放在事件循环队列中(那一时刻的队尾)。

补充一点,定时器执行时间总是比我们要求的多一点的原因是:因为在定会器的回调在队列的位置之前的位置很有可能有其他的回调,而且必须等待他们执行完毕后定时器的回调才能执行。

异步操作也会带来诸多问题
比如多个异步操作所操作的对象之间有某种关系、有一定的顺序要求,或者一个异步任务的发生与另一个异步任务的回调同时到达等等

比如:

var a,b;
function foo(x){
    a=x+2;
    baz();
}
function bar(y){
    b=y+2;
    baz();
}
functionbaz(x){
    console.log(a+b);
}
ajax(xxx,foo);
ajax(xxx,bar);

我们无法知道哪个异步的回调先返回到javascript引擎,所以结果不确定
虽然我们可以通过判断,a、b都有值时才调用baz,但显然是很麻烦的,(在之后Promise那会解决这些问题)

ES6从本质上改变了在哪里管理事件循环,它精确指定了事件循环的工作细节,这意味着在技术上将其纳入了javascript引擎的势力范围,而不是只由宿主环境来管理。这个改变主要原因是ES6中Promise的引入,因为这项技术要求对事件循环队列的调度运行能够直接进行精细控制。

任务队列:
建立于事件循环队列之上(Promise的异步特性基于此),是挂在事件循环队列的每个tick之后的一个队列,在事件循环的每个tick中,可能出现的异步动作不会导致一个完整的新事件添加到事件循环队列中,而会在当前tick的任务队列末尾添加一个项目(任务)

举个栗子,事件循环队列类似于一个游乐园游戏,玩过了一个游戏后,你需要重新到队尾排队才能再玩一次;而任务队列类似于玩过了游戏之后,插队接着继续玩。

再举栗

console.log('A');

setTimeout(function(){console.log('B')},0);

//任务队列中
shedule(function(){console.log('C')});
shedule(function(){console.log('D)});

结果:A C D B

因为首先会执行主线程中顺序执行(非异步)的代码,所以先打印A接着顺序执行,发起一个定时操作,宿主环境安排的负责监听定时的线程将回调放入处于事件循环队列中的任务队列的末尾(因为定时为0 所以立刻响应)然后将任务队列中的回调依次执行,所以打印C、D、B

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant