一、javascript的语言和执行环境是单线程
    浏览器js引擎中负责解析和执行js代码的线程只有一个,所以同一时间只能执行一个任务,所有任务都需要排队。
    为什么会被设计为单线程?
    (1)js设计之初,并不流行多进程多线程的语言,且当时硬件支持并不好(出生环境
    (2)其次因为多线程的复杂性,多线程操作需要枷锁,编码难度增加(使用难度,最初只用来完成简单的用户交互比如表单验证)
    (3)js会操作dom,多线程同时操作同一dom会造成冲突。

    二、同步与异步
    js是单线程,所有任务都会排队,如果某个任务很耗时,则会堵塞其他排队的任务(扫二维出地铁站时,手机信号不好时,应该到旁边等二维码显示出来再出站 => 让能执行的任务先执行,不要一根筋卡在原地 =>网页请求多张图片时,不可能让图片排队加载,吃个饭回来页面可能都还没加载好)。所以js设计之初分为了同步和异步任务。
    (1)同步任务,直接在主线程排队执行
    (2)放在任务队列(event queue)中,等待同步任务执行完了再进入主线程执行。

    常见使用异步任务的场景:
    (1)网络请求:ajax请求、图片加载(任务可能会很耗时)
    (2)setTimeout、setTimeInterval(本来就需要等待的任务)
    (3)事件绑定:按钮绑定事件以后,用户可能点也可能不点(执行时间不确定)
    (4)es6中的promise和es7的async与await

    三、js事件循环机制
    js中的单线程与异步 - 图1
    (1)同步任务直接进入主线程排队执行
    (2)异步任务进入event table,等时间到了进入event queue排队。
    setTimeout(()=>{},1000),要等1000毫秒后才能进入event queue排队
    (3)等主线程的任务执行完以后,处于空闲状态,会去读取event queue的任务队列,如果有任务,则进入主线程执行。

    注意点:
    异步任务不会准时执行
    (1)就算异步任务在两秒后进入event queue,也要等待主线程中完同步任务执行完后,再进入主线程执行。
    (2)setTimeout和setInterval每次调用的时间间隔至少为4毫秒,这是因为函数嵌套(嵌套层级深)或setInterval回调函数阻塞的原因。

    1. function test(){
    2. console.time('test')
    3. function test1(){
    4. function test2(){
    5. function test3(){
    6. function test4(){
    7. console.timeEnd('test')
    8. }
    9. test4()
    10. }
    11. test3()
    12. }
    13. test2()
    14. }
    15. test1()
    16. }
    17. test()
    18. // => test: 0.04296875 ms

    四、setTimeout和setInterval
    setTimout接受连个参数,一个是回调函数,一个是进入event queue的时间
    setInterval也接受两个参数,一个回调函数,一个是向event queue中添加任务的间隔时间。
    这两个定时器函数调用时会返回表示该超时排期的id,可用来清除这个超时任务。真正生产环境中,setInterval中每个回调函数执行时间不定所以可能会有阻塞,并不能保证时间间隔,最好不要用。

    图片加载案例

    1. function imgLoad(filePath,success, fail){
    2. let img = new Image()
    3. img.src = filePath;
    4. img.onload = success(img)
    5. img.onerror = fail(new Error('加载失败'))
    6. }
    7. imgLoad('XXX.png',
    8. (img)=>{
    9. let body = document.getElementByTagName('body')
    10. body.appendChild(img)
    11. },
    12. (error)=>{
    13. console.log(error)
    14. }
    15. )