了解js的事件执行顺序,首先明白两组概念:
同步任务/非耗时任务:指的是在主线程上排队执行的任务,只有前一个执行完毕才能执行后一个任务。
异步任务/耗时任务:由js委托给宿主环境进行执行,执行完后会通知js主线程执行异步任务的回调函数。
同时呢,异步任务又分为宏任务和微任务:
js的事件执行顺序是这样的:
语言描述:
js是单线程的,从上到下执行:
从上到下执行,同步任务执行,异步任务扔到任务队列里等待。所有同步任务执行完,去到任务队列里。
从上到下执行宏任务,new promise实例的时候是同步任务,所以promise里的语句立即执行,.then()里面的语句是微任务,放到微任务队列。每当有一个宏任务执行完毕就检查是否有微任务,有就把微任务先执行。一直这样循环。
举个例子:两个人去银行办业务,排号,一个人办理完他需要的任务,然后业务员问他还有没有别的需求,如果这时候他提出了其他,还是会给他办理。等他的事情都办完了才能轮到下一个人办理业务。
首先,从上往下看,console同步任务,执行,输出1;
setTimeout异步任务,扔到消息队列;
new一个Promise实例,立即执行,输出5。发现.then()里有微任务;
下面还是一个setTimeout异步任务,继续扔到消息队列;
主线程所有任务执行完了,检查一下是否有微任务,有,输出6;
开始处理第一个(2中的setTimeout)异步任务,输出2;new promise立即执行,输出3,发现.then()里面的微任务;
发现当前宏任务执行完毕,后面没有语句了,只有另一个宏任务,把微任务执行完再去处理,输出4;
执行第二个异步任务,输出7,new promise立即执行,输出8,发现.then()里面的微任务;执行完当前宏任务了,输出9
正确顺序156234789
经过我上午对另一个例子的验证,发现process.nextTick()的执行优先于setTimeout
这个里面是加了process.nextTick()的,还加了setTimeout(fn, 0)
和setImmediate
分析:
先执行同步任务,输出1;
第二个同步任务就是test函数,先执行,输出2;再执行new promise了,输出p1,检查到有一个.then()微任务。(!!!记住,我们现在在执行同步任务呢!!!),继续往下,一个异步任务,扔到任务队列。再走,输出3;又一个setImmediate,扔到队列;
test执行完了,继续向下,输出4;
这时候同步任务都处理完了,轮到了这个微任务,输出p1.then;但是!!!nextTick是每当事件循环进行一次完整的行程时,在下一个事件循环滴答开始之前调用。在这里发现它的优先级高于.then,于是先输出nextTick;再输出P1.then;
接下来就是setTimeout(fn, 0)和setImmediate的优先级问题了。建议参考https://www.cnblogs.com/dennisj/p/12550996.html这位博主的文章,总结出来这段就是
setImmediate
和setTimeout(fn, 0)
哪个回调先执行,需要看他们本身在哪个阶段注册的,如果在定时器回调或者I/O回调里面,setImmediate
肯定先执行。如果在最外层或者setImmediate
回调里面,哪个先执行取决于当时机器状况。
他们等于都在最外层,取决于机器状况,但是我试了几次,都是定时器t1先执行的,可能因为setImmediate里运行的比较多,需要时间。所以输出t1;(这里并不确定,是基于结果的,欢迎指正)
然后进到setImmediate2里面,从上到下,输出setImmediate;输出s2;new promise实例,输出s2.p2,发现微任务,执行完当前函数(类似于前面的道理),输出s2end;
完成微任务,输出p2.then
结果是 1 2 p1 3 4 nextTick P1.then t1 setImmediate s2 s2.p2 s2end p2.then
还是这个图,我们在31行后加入一个setTimeout(time2, 0),打印的是新增的time。目的是验证同在setImmediate里的setImmediate和setTimeout的执行顺序。
setImmediate(() *=>* {
console.log('setImmediate')
setImmediate(()*=>*{
.......
})
setTimeout(time2, 0)
})
setTimeout(time1, 0)
}
可以发现,输出结果符合上面那位博主说的:如果在setImmediate`回调里面,哪个先执行取决于当时机器状况。
同样的,我也试了把最外层的setImmediate改成setTimeout,确实是setImmediate优先执行。
js中的异步任务
Events:javascript各种事件的执行都是异步任务
setTimeout、setInterval 定时器
queueMicrotask 执行微任务
Ajax
requestAnimationFrame 类似于定时器,但是是以每一帧为单位
fetch Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分
MutationObserver 创建并返回一个新的 MutationObserver 它会在指定的DOM发生变化时被调用。
Promise(但是promise会立即执行,.then里面的内容算作微任务)
async function
默认导出 export default 只能使用一次,按需导出export可以使用多次
默认导入时接收名称可以自定义任何合法名称,按需导入的名字必须和按需导出的名字一致,按需导入可以用as重命名
默认导入和按需导入可以同时使用
如果只单纯想执行某个模块中的代码,不需要得到成员,可以不导出,直接用import导入文件路径就可以
解决回调地狱问题:嵌套太多,不方便维护代码。可读性差
基于回调函数形式读取内容:node.js官方提供的fs模块仅支持以回调函数的方式读取文件,不支持Promise的调用方式。因此,需要先安装then-fs这个第三方包,从而支持我们基于Promise的方式读取文件内容;然后,导入这个模块;实例对象调用readFile方法并.then()设置成功函数
.then()可以链式调用,只要上一个结果是Promise实例,就可以继续用.then()方法
.catch()用来捕获错误
Promise.all()会发起并行的Promise异步操作,等所有的异步操作全部结束后才会执行下一步的.then操作(等待机制)
获取.then的两个实参:resolve、reject;失败 reject(err); 成功 resolve(data)
ES8的语法,简化Promise异步操作。在async/await出现前,只能用链式.then()方式处理Promise异步操作
读取的本来是一个promise实例对象,内容需要用.then获取,但是用了async和await之后就直接出现了结果。
方法内用到了await,方法外就需要用async修饰;在async方法中,第一个await之前的代码会同步执行,await之后的代码会异步执行
js是单线程执行的编程语言,为防止某个耗时的任务导致程序假死的问题,js把待执行任务分两类:
同步任务/非耗时任务
指的是在主线程上排队执行的任务,只有前一个执行完毕才能执行后一个任务
异步任务/耗时任务
由js委托给宿主环境进行执行,执行完后会通知js主线程执行异步任务的回调函数
是单线程执行的编程语言,为防止某个耗时的任务导致程序假死的问题,js把待执行任务分两类:
同步任务/非耗时任务
指的是在主线程上排队执行的任务,只有前一个执行完毕才能执行后一个任务
异步任务/耗时任务
由js委托给宿主环境进行执行,执行完后会通知js主线程执行异步任务的回调函数
js主线程从任务队列中读取异步任务的回调函数,放到执行栈中依次执行。这个过程循环不断,这种运行机制又叫EventLoop