itstrive / striveCode

some demo and js Knowledge points records :cn: :cloud: :snowflake:
108 stars 83 forks source link

js异步题目(浅析) #18

Open itstrive opened 5 years ago

itstrive commented 5 years ago

先看一个题目

function fn(n){
    fn(n);
}
fn(1);

这个题目的运行结果毫无疑问, 内存溢出, Maximum call stack size exceeded

如果稍微变一下呢?

function fn(n){
    setTimeout(()=>fn(n),0)
}
fn(1);

这个改变,会发生什么呢?这时候不会内存溢出了。


以上题目能解释明白的,应该很懂异步

简单说,js异步其实是宿主环境赋予给js的一种能力。采用 Event loop的形式去 callback queue(回调队列)取异步任务到 call stack中执行。

看以下图,这个需要理解了就ok了。

image

js是单线程的,浏览器为那些异步任务单独开辟了一些线程(比如http请求线程,浏览器定时触发器,浏览器事件触发线程),当然我们可以统称为 webAPIs。

因为异步的情况会很多,所以会放到一个叫 callback queue 队列里面(可以叫异步任务队列)。

堆(heap)和栈(stack)共同组成了js主线程,函数的执行就是通过进栈和出栈实现的。

比如图中有一个fn()函数,主线程把它推入栈中,在执行函数体时,发现还需要执行上面的那几个函数,所以又把这几个函数推入栈中,等到函数执行完,就让函数出栈。等到stack清空时,说明一个任务已经执行完了,这时就会从callback queue中寻找下一个人任务推入栈中(这个寻找的过程,叫做event loop,因为它总是循环的查找任务队列里是否还有任务)

setTimeout(fn1, 0)

请问fn1会不会立刻执行,答案是不一定。需要看下主线程内是否执行完毕了。如果有程序会先执行

比如:

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

会先输出2,然后1

你知道为何加上定时器,就不会内存溢出了么?因为压根不在js主线程内,所以每次都是避开避开。