Javascript异步编程1

195 阅读4分钟

进程与线程

  • 单线程Js怎么实现异步的
    通过浏览器内核多线程实现异步

  • 进程
    1、cpu资源分配的最小单位
    2、程序运行的实例
    3、同一程序可以产生多个进程
    4、一个进程包含一个或者多个线程

  • 线程
    1、操作系统能够进行运算调度的最小单位
    2、一次只能执行一个任务
    3、有自己的调用栈、寄存器环境
    4、同一进程的线程共享资源分享

  • 浏览器内核进程、线程

线程名 描述
GUI线程 渲染布局
JS引擎线程 解析、执行JS与GUI互斥线程
定时器触发线程 setTimeout setInterval
事件触发线程 将满足触发条件的事件放入任务队列
异步HTTP请求线程 XHR所在线程

异步队列

  • JavaScript的执行顺序

setTimeout(() => {
    console.log('s1');
    new Promise((resolve,reject)=>{
        resolve();
    }).then(function(){
        new Promise((resolve,reject)=>{
            resolve();
        }).then(function(){           
            console.log('t4');
        })
        console.log('t2');
    })
}, 0);
new Promise(function(resolve){
    console.log('p1');
    resolve();
}).then(function(){
    console.log('t1');
})
setTimeout(function(){
    console.log('s2');
})
console.log(2);

// 分析代码执行书序
// 1、将第7行的setTimeout入宏队列,执行到21行,打印出p1,将23行then方法入微队列,将26行settimeout入宏队列,打印2    这次打印:p1  2
// 2、询问异步队列中的微队列,执行24行的微任务,打印出t1,微队列清空了   这次打印  t1
// 3、询问异步队列中的宏任务,执行第7行的setTimeout,打印出s1  将第11行then入微队列   这次打印s1
// 4、微队列不为空,执行第11汗微队列,打印出t2,将15行入微队列  这次打印t2
// 5、微队列不为空,执行第15汗微队列,打印出t4    这次打印t4
// 6、微队列清空了,执行第26行的宏任务,打印出 s2  这次打印s2
// 7、异步队列执行完毕,程序执行完毕

// 结果为:p1  2   t1  s1  t2  t4  s2 

  • 宏任务与微任务
  1. 微任务:Promise.then、 process.nextTick、Object.observe、MutationObserver、postMessage
  2. 宏任务:整体代码script、setTimeout、setInterval、setImmediate、I/O、UI Rendering

异步场景

1、定时器
2、网络请求
3、事件绑定
4、ES6 Promise

  • 定时器异步任务原理

1、调用webAPI
2、定时器线程计数2s
3、事件触发线程将定时器事件放入任务队列
4、主线程通过Event Loop遍历任务队列
5、任务队列是由事件触发线程往里面加,由Event Loop往外推

  • 定时器问题
    1、定时器任务可能不会按时执行
    2、定时器嵌套5次之后最小间隔不能低于4ms

  • 定时器应用场景
    1、防抖
    2、节流
    3、倒计时
    4、动画

常见的一个关于块级作用域和定时器的问题

 for (var i = 0; i <= 10; i++) {
            setTimeout(() => {
                console.log(i);
            }, 1000 * i);
        }
        //打印出 11 11 11 11 11 11 11 11 11 11 11
        for (let i = 0; i <= 10; i++) {
            setTimeout(() => {
                console.log(i);
            }, 1000 * i);
        }
        //打印出 0 1 2 3 4 5 6 7 8 9 10 11
        for (var i = 0; i <= 10; i++) {
            (function(i) {
                setTimeout(() => {
                    console.log(i);
                }, 1000 * i);
            })(i);
        }
         //打印出 0 1 2 3 4 5 6 7 8 9 10 11

Event Loop机制

浏览器端
  • 异步事件
    1. 宏观:浏览器多线程
    2. 微观:Event Loop,时间循环;
async function aync1(){
    console.log('async1 start')
    await async2();
    console.log('async1 end');
}
async function async2(){
    return Promise.resolve().then(()=>{
        console.log('async2 promise');
    })
}
console.log('start');
setTimeout(() => {
    console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve){
    console.log('promise1')
}).then(function(){
    console.log('promise2')
});
// 1、同步代码块先执行,打印出start;
// 2、执行到12行时,将该函数放到宏任务;
// 3、执行到第15行的时候,打印出async1 start;然后调用async2,将第8行放到微任务,第4行会在第8行执行完以后立即执行;
// 4、执行到第16行打印出promise1,将第19行放到微任务队列;
// 5、目前打印出的是:start  async1 start  promise1;
// 6、查看微任务队列,执行第8行和第19行,打印出: async2 promise  promise2  aync1 end;
// 7、调用第12行和4行的宏任务,分别打印出:setTimeout    
// 8、最后打印结果为:start  'async1 start'  promise1 'async2 promise'  promise2     'aync1 end'  setTimeout
Node端

  • 六个阶段
  1. timers:执行timer的回调;
  2. pending callbacks:系统操作的回调;
  3. idle、prepare:内部使用;
  4. poll:等待新I/O事件;
  5. check:执行setImmediate回调;
  6. close callbacks:内部使用;
  • poll阶段的两个主要功能:
  1. 计算应该被block多久;
  2. 处理poll队列的事件;

const fs = require('fs');
function someAsyncOperation(callback){
    fs.readFile(__dirname,callback);
}
const timeoutScheduled = Date.now();
setTimeout(() => {
    const delay = Date.now()-timeoutScheduled;
    console.log(`${delay}ms have passed since I was scheduled`);
}, 100);
someAsyncOperation(()=>{
    const startCallback = Date.now();
    while (Date.now()-startCallback<200) {
        //do nothing
    }
});

输出结果图:

执行顺序:

  1. 首先执行fs.readFile,花费时间2ms;
  2. 再执行callback,花费时间200ms;
  3. setTimeout时间到了并执行,所以打印出了202ms;
  • process.nextTick() 是一个异步的nodeAPI,但不属于event loop的阶段,node执行到该方法时event loop停下来,立即执行nextTick中的内容
const fs = require('fs');
fs.readFile(__filename,_=>{
    setTimeout(() => {
        console.log('setTimeout')
    }, 0);
    setImmediate(_ =>{
        console.log('setImmediate');
        process.nextTick(_ =>{
            console.log('nextTick2')
        })
    })
    process.nextTick(_ =>{
        console.log('nextTick1')
    })
})