初步浅析浏览器异步机制

2017-01-11  本文已影响0人  BertieSama

先说一下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.

上一篇 下一篇

猜你喜欢

热点阅读