先说一下微任务:
微任务队列:
异步任务需要适当的管理。为此,ECMA 标准规定了一个内部队列 PromiseJobs ,通常被称为“微任务队列(microtask queue)”(ES8 术语)
-
队列(queue)是先进先出的:首先进入队列的任务会首先运行。 -
只有在 JavaScript 引擎中没有其它任务在运行时,才开始执行任务队列中的任务。 https://zh.javascript.info/microtask-queue
“任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列”,就是读取里面有哪些事件。https://www.ruanyifeng.com/blog/2014/10/event-loop.html
? ——阮一峰
我的理解:微任务队列其实就是一些事件的傀儡队列,这个傀儡就是回调函数,当主线程任务完成之后,开始读取微任务队列中的事件时,就是去执行事件对应的回调函数。
ps:(可以想象成一个工作狂,他会提前准备一个框框(微任务队列)放一些当前任务中可以慢慢处理的其他任务,然后先去做他当前的主要的工作,做完主要工作就回去检查这个框框,这个框的任务也做完了,就去问老大(宏任务队列)还有没有下一个任务(下一个宏任务))
微任务中也可以有微任务,所以会有无限套娃的风险。https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_DOM_API/Microtask_guide
promise中的then方法:
-
明确then的回调函数是什么时候进入微任务队列的
思否:https://segmentfault.com/q/1010000022578087 我觉得这个答案更符合我的理解: then的回调函数什么时候进入PromiseJobs取决于then方法前一个promise的状态, 如果调用 then 时 promise 是 pending 状态,回调会进入 promise 的 [[PromiseFulfill/RejectReactions]] 列表里;否则会进入 PromiseJobs。
看例子:
eg1(调用then的promise状态为pending):
new Promise(()=>{}).then((res)=>{console.log("我是then"+res)})
console.log("我是同步的")
输出:
"我是同步的"
结论:如果调用 then 时 promise 是 pending 状态,回调会进入 promise 的[[PromiseFulfill/RejectReactions]] 列表里等待执行;
eg2(调用then的promise状态不为pending):
new Promise((res)=>{res(1)}).then((res)=>{console.log("我是then:"+res)})
console.log("我是同步的")
输出:
"我是同步的"
"我是then:1"
结论:
如果调用 then 时 promise 不是 pending 状态,则会进入 PromiseJobs,待同步任务执行完,则执行PromiseJobs中的任务
eg3:
new Promise((res)=>{}).then(console.log("我是then"))
console.log("我是同步的")
输出:
"我是then"
"我是同步的"
思考:调用then的promise是pending状态,为什么"我是then"还是会打印?👇
eg4:
new Promise((res)=>{res(1)}).then(console.log("我是then"))
console.log("我是同步的")
输出:
"我是then"
"我是同步的"
思考:????为什么还是会打印👇
👇👇👇
-
then方法的参数 then方法接受两个参数(onFulfilled, onRejected)
-
If onFulfilled is not a function, it must be ignored. -
If onRejected is not a function, it must be ignored. ? 文档——https://promisesaplus.com/
上面说的很清楚,如果接受的参数不是一个函数,那么必须被忽略(不管他)所以会有值穿透的情况 好,再来看看上面的思考: new Promise(()=>{}).then(console.log("我是then"))
console.log("我是同步的")
首先明确一点:console.log是一个方法,也就是一个函数,所以你再看看你then里面传的是什么,是console.log("xxxxxx"),所以你在这里同步立即执行这个函数,然后把这个函数的返回值传到then里面去,所以相当于你在then里面传了一个值undefined
不信,我们来看看:
new Promise((res)=>{res(1)}).then(console.log("我是then"))
console.log("我是同步的")
这个输出和上面一样,但是不一样的是,调用then的promise有结果 1 ,且状态不是pending
所以就算中间的then会被忽略,再调用一个正常then还是可以接受到数据
new Promise((res)=>{res(1)})
.then(console.log("我是then"))
.then(res=>{console.log("promise的结果:"+res)})
console.log("我是同步的")
输出:
"我是then"
"我是同步的"
"promise的结果:1"
不信再来:
new Promise(()=>{})
.then((function(){console.log(2)})())
.then(res=>{console.log(res)})
console.log("我是同步的")
输出:
2
"我是同步的"
好了,到了这里应该都明白了,接下来,看一下then添加回调函数的写法:https://wangdoc.com/javascript/async/promise.html#promiseprototypethen
let func = function() {
return new Promise((resolve, reject) => {
resolve('我是老大new Promise');
});
};
let cb = function() {
return '我是回调函数的返回值';
}
func().then(function () {
return cb();
}).then(resp => {
console.warn(resp);
console.warn('1 =========<');
});
func().then(function () {
cb();
return undefined
}).then(resp => {
console.warn(resp);
console.warn('2 =========<');
});
func()
.then(cb())
.then(resp => {
console.warn(resp);
console.warn('3 =========<');
});
func().then(cb)
.then(resp => {
console.warn(resp);
console.warn('4 =========<');
});
内部封装大致如下:👇👇
.then(new Promise((res,rej)=>{
res(cb());
}))
console.log()和console.log是不一样的
then方法内部会进行一个判断:看是否是函数
-
当then里面传入console.log时,相当于传入了一个函数log,于是promise的函数执行器就会帮你调用new Promise(),去调用这个函数, 传入console.log时,在then中相当于发生如下过程
new Promise(res=>res(1)).then(()=>{
return new Promise((res,rej)=>{
res(console.log());
})
})
-
当then里面传入console.log()时,相当于传入了一个非函数,也就是console.log()这个函数的返回值undefined,所以会发生值穿透,所以上面第二种情况,先打印2,再打印new Promise中的res的参数值
- then的回调方法的注册时机(事件机制是先注册再执行)
注册时机其实还是看调用then的promise状态,只要promise状态改变了,其调用的then的方法就会被注册,但是还没有被执,执行再按照注册顺序进行
https://juejin.cn/post/6844903987183894535
|