先说一下JavaScript异步的概念:
首先因为js是单线程的语言,且没有异步的特性。
然后js的宿主环境是在浏览器端,js的异步操作是浏览器来提供的,浏览器的js引擎在执行js代码的时候也只是会提供一条线程去执行,真抠...并且!js(主栈列内的js,后文解析什么是主栈列)代码执行会阻塞渲染线程和浏览器事件触发线程,后面这两个线程是属于浏览器内的常驻线程,当然还有其他的比如说http请求线程,那么比如说我们js代码内有一个是改变某个DOM元素样式的代码,执行了之后只是记录下这个渲染事件,不会去切换渲染线程进行渲染,而是继续执行剩下的主栈列内的代码,好烦..发现又要解析主栈列和任务队列的概念..可以去看阮一峰的event loop解析,就是说,算了上代码吧:
$.ajax({
type:'get',
url:url,
beforeSend:function() {
console.log('发送请求了,别看我在ajax内,其实跟它没什么关系')
},
success:function(data){
console.log('ajax执行完毕,我比setTimeout的事件优先级要高')
},
})
setTimeout(function() {
console.log('定时器也完了')
},0)
console.log('我在主栈列,让我先走')
//触发顺序
1.发送请求了,别看我在ajax内,其实跟它没什么关系
2.我在主栈列,让我先走
3.jquery-1.9.1.min.js:5 XHR finished loading: GET "http://localhost:2000/123".
4.ajax执行完毕
5.定时器也完了
js引擎在解析js代码的时候,遇到settimeout和setInterval或者ajax都会先告诉它们缓缓,你们先进任务队列里面等等,我执行完剩下在主栈列内的代码先。
然后执行完主栈列的代码之后,js线程完事之后,并不会马上去任务队列内找任务放到主栈列,而是..先查找刚刚是不是有需要渲染引擎要干的是..比如说你要改某个DOM元素的背景色,如果有的话,那么就会切换渲染线程,先把页面的渲染需要的repaint和reflow做了,关于repaint和reflow在这里不做陈述,有兴趣google一下吧。
那让渲染引擎做完了渲染工作,浏览器才回去查找任务队列有没有人挂号,采取的是轮询机制,跟医生看病人差不多,就是说一个一个来,随便说一句ajax比settimeout和setInterval优先级要高的,比如说执行完ajax事件1,就回去看任务队列?nonono,先看有没有渲染引擎要做的事,就是说再走一次刚刚主栈列的流程,没有渲染的事了?那就下一位病人吧。
回到主题...为什么我们需要异步,因为刚刚前文所说的js阻塞,如果说ajax是同步执行的..那么就会等服务器响应我的请求,拿到数据再做其他事情,那如果服务器不行请求很久那岂不是很low,那么浏览器干了什么就是在走到ajax的时候,在把它推到任务队列之前,起一个http线程,去发送请求,请求完成了,再讲ajax事件推到任务队列的末端,从而实现异步,在说一句就是根据上面所说的其实settimeout和setInterval要比预期的事件要多一点的,因为在队列中,不是即刻执行,明天再更新。
好吧,干货来了,在日常的写(lu)作(ma)中我们应该怎么样优(zuo)雅(si)地利用异步机制去完成我们想要的异步操作呢:
(一)welcome to callback hell(回调函数)
function getSomething(fn){
var num=0;
settimeout(function(){
num=1;
fn(num);
})
}
function compute(x) {
alert(x * 2);
}
getSomething(compute);//任性,不喜欢大小写命名法
如上文所说,settimeout内的js代码都进入了主栈列,那么执行顺序就是从上至下了,但是在复杂的需求下这种方法会产生callback hell,并且还不容易看出详细异步步骤,不简单的逻辑的话可以用用。
(二)promise
function getSomething() {
var r = 0;
return new Promise(function(resolve) {
setTimeout(function() {
r = 2;
resolve(r);
}, 10);
});}
function compute(x) {
alert(x * 2);
}
getSomething().then(compute);
//没有接触Promise的朋友可以去看看Promise的使用,因为一时半会又讲不完...本文仅提供异步方法
(三)generator
function getSomething() {
var r = 0;
setTimeout(function() {
r = 2;
it.next(r);
}, 10);
}
function *compute(it) {
var x = yield getSomething();
alert(x * 2);
}
var it = compute();
it.next();
//我只负责提供demo......详细可以去看es6的教程
(四)promise + generator
function getSomething() {
var r = 0;
return new Promise(function(resolve) {
setTimeout(function() {
r = 2;
resolve(r);
}, 10);
});
}
function *compute() {
var x = yield getSomething();
alert(x * 2);
}
var it = compute();it.next().value.then(function(value) {
it.next(value);
});
(五)async (ES6都玩腻了,ES7还远吗,异步操作终极大法)
function getSomething() {
var r = 0;
return new Promise(function(resolve) {
setTimeout(function() {
r = 2;
resolve(r);
}, 10);
});}
async function compute() {
var x = await getSomething();
alert(x * 2);
}
compute();
困死本宝宝了..1点了。
好咯,晚安,world peace.