函数对象
函数对象:将函数作为对象使用,就是函数对象
function Person(){
}
Person.age = 18
但是函数对象不能追加Person.name属性,每一个函数对象都有自身的name属性,返回的是函数对象定义的名字:Person.
回调函数的分类
回调函数分为同步、异步。
同步回调:
1.立即在主线程上执行,不会放入回调队列中。比如【数组遍历相关的回调函数】,【Promise的executor函数】。
//同步的回调函数
let arr = [1,3,5,7,9]
arr.forEach((item,index)=>{
console.log(item)
})
console.log('主线程代码')
异步回调: 1.不会立即执行,会放入回调队列中以后再执行。比如【定时器回调】、【ajax回调】、【Promise的成功和失败的回调】。
//异步的回调
setTimeout(()=>{
console.log('@')
},0)
console.log('主线程')
就算定时器回调是0秒,他仍然是异步回调,仍然是先执行主线程、再执行队列的。
错误类型说明
错误的类型:
1.Error:所有错误的父类型,如果出错、会提示下方的具体错误类型,而不是Error。
2.ReferenceError:引用的变量不存在;
3.TypeError:数据类型不正确。
4.RangeError:数据值不在所允许的范围内。
5.SyntaxError:语法错误。
回顾捕获错误:catch(..)参数有两个属性:message错误相关信息和stack记录信息
try{
console.log('try中书写可能存在错误的代码')
}catch(error){
console.log('将try中发生的错误捕获到:',error)
error.message
error.stack
}
回顾抛出错误:throw是我们自己知道有错,提示的信息
if(...){
}else{
//Error()是一个内置对象
throw new Error('报错啦')
}
初始Promise
Promise是异步编程的新方案(旧方案是--回调函数)。
从语法上来说:Promise是一个内置构造函数。
从功能上来说:Promise对象用来封装一个异步操作,并可以获取其成功/失败的值。
-
Promise不是回调,是一个内置的构造函数,是程序员自己new出来调用的。
-
new Promise()的时候,要传入一个回调函数,这个回调函数是【同步的】,会立即在主线程上执行,它被称为【executor】函数。
-
每一个Promise实例都有3种状态:
初始化(pending)、成功(fulfilled)、失败(rejected)。 -
每一个Promise实例在刚刚被new出来的那一刻,状态都是
初始化(pending)。 -
executor函数会接收到2个参数,他们都是【函数】,分别用形参:
resolve、reject接收。
5.1 调用【resolve】,会让Promise实例状态变为:成功(fulfilled),同时可以指定成功的value。
5.2 调用【reject】,会让Promise实例状态变为:失败(rejected),同时可以指定失败的reason
//创建一个Promise实例对象
const p = new Promise((resolve,reject)=>{
resolve('ok')
})
Promise的基本使用
-
重要语法:
new Promise(executor)构造函数
Promise.prototype.then方法(原型对象) -
基本编码流程 2.1 创建Promise的实例对象(pending状态),传入executor函数
2.2 在executor中启动异步任务(定时器、ajax请求)
2.3 根据异步任务的结果,做不同的处理:
如果异步任务成功:我们调用resolve(value),让Promise实例对象状态变为成功(fulfilled),同时指定成功的value
如果异步任务失败:我们调用reject(reason),让Promise实例对象状态变为失败(rejected),同时指定失败的reason。
2.4 通过then方法,为Promise的实例指定成功、失败的回调函数,来获取成功的value、失败的reason。注意:then方法所指定的成功、失败的回调,全部都是【异步的回调】。
关于状态的注意点:
-
三个状态: pending:未确定的---初始状态
fulfilled:成功的---调用resolve()后的状态
rejected:失败的---调用reject()后的状态 -
两种状态的改变 pending---》fulfilled
rejected---》rejected -
状态只能改变一次!!!(不能既有resolve,又有reject)
-
一个promise可以指定多个成功/失败的回调函数(then),
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('成功返回的数据')
},1000)
})
p.then(
//回调中可以接收resolve或者reject的参数
(value)=>{console.log('成功了',value)},//成功的回调--异步
(reason)=>{console.log('失败了',reason)}//失败的回调--异步
)
p.then(
//回调中可以接收resolve或者reject的参数
(value)=>{console.log('成功了',value)},//成功的回调--异步
(reason)=>{console.log('失败了',reason)}//失败的回调--异步
)
Promise中的resolve和reject返回的是一个值,值交给了p.then中的具体回调函数。
与ajax配合使用
const p = new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
if(xhr.status>=200&&xhr.status<300){
//如果进入到这个判断了,说明响应到了,可以写resolve逻辑
resolve(xhr.response)
}else{
//请求到了,但是状态码不是2xx,说明请求的有点问题
reject('请求出错了')
}
}
}
xhr.open('GET','https://api.apiopen.top/getJoke')
xhr.responseType = 'json'
xhr.send();
})
//resolve或者reject后执行的函数
p.then(
(value)=>{console.log('成功了',value)},
(reason)=>{console.log('失败了',reason)}
)
封装ajax请求
定义一个sendAjax函数,对xhr的get请求进行封装;
该函数接收两个参数:url(请求地址)、data(参数对象)
该函数返回一个Promise实例:
如果ajax请求成功,则Promise实例成功,成功的value是返回的数据。
如果ajax请求失败,则Promise实例失败,失败的reason是错误提示。
function sendAjax(url,data){
return new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = ()=>{
if(xhr.readyState===4){
if(xhr.status>=200&&xhr.status<300){
resolve(xhr.response)
}else{
reject('请求出问题')
}
}
}
//整理参数
let str = ''
for(let key in data){
str += `${key}=${data[key]}&`
}
//从最后一个开始,删除1个
str = str.slice(0,-1)
xhr.open('GET',url)
xhr.responseType = 'json'
xhr.send()
})
}
发送数据:
const x = sendAjax('http://api.apiopen.top',{page:1,count:2,type:'video'})
//x是Promise返回的对象,用来进行判断成功与失败的回调
x.then(
(value)=>{
console.log('成功了',value)
},
(reason)=>{
console.log('失败了',reason)
}
)
Promise_prototype_catch
Promise.prototype.catch:Promise实例.catch(onRejected) 。专门用于:只想要失败的回调,不想要成功的回调。
onRejected:这个方法专门用来传入失败的回调函数(reason)=>{}
说明:catch方法是then方法的语法糖(和then的效果一样,只是写法不一样。表达意思相同),等价于:then(undefined,onRejected)第一个不传,但是必须写undefined,占位。
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(-100)
},1000)
})
p.then(
undefined,
(reason)=>{
}
)
等价于=》
p.catch(
reason=>{console.log('失败了',reason)}
)
Promise.resolve
Promise:resolve(value):用于快读返回一个状态为fulfilled或rejected的Promise实例对象。
value的值可能是:非Promise值、Promise值
这个方法没有在【原型对象】上,所以他是给自己用的,而不是给【实例对象】用的。
const p = new Promise((resolve,reject)=>{
resolve(100)
})
p.then(
(value)=>{
},
(reason)=>{
}
)
如果你就是想输出成功的内容,那么不需要这么麻烦,直接通过Promise.resolve就可以了。
const p = Promise.resolve(100)
p.then(
(value)=>{
console.log('成功啦',value)
},
(reason)=>{
}
)
Promise.reject
Promise.reject()用于快速返回一个状态为:rejected的Promise实例对象
const p = Promise.reject(900)
p.then(
value=>console.log('成功了',value)
reason=>console.log('失败了',reason)
)
Promise.all和Promise.race
Promise.all(promiseArr):其中promiseArr包含n个Promise实例的数组。该方法用来返回一个新的Promise实例,只有所有的Promise都成功了,才成功。只要有一个失败了,就直接失败。
const p1 = Promsie.resolve(a)
const p2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('b')
})
},500)
const p = Promise.all([p1,p2])
p.then(
value=>console.log('成功啦',value)
reason=>console.log('失败了',reason)
)
Promise.all中存储的都是成功的Promise实例,返回的是成功Promise实例返回的value,上方返回的是['a','b']。
const p1 = Promsie.resolve(a)
const p2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('b')
})
},500)
const p = Promise.all([p1,p2])
p.then(
value=>console.log('成功啦',value)
reason=>console.log('失败了',reason)
)
如果失败了,返回的就是具体哪一条失败的回调函数。
Promise.race(promiseArr):返回一个新的Promise实例,成功还是失败?要根据最先出结果的promise为准。
const p1 = Promise.resolve('a')
const p2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('b')
},500)
})
const p = Promise.race([p1,p2])
p.then(
value=>console.log('成功啦',value)
reason=>console.log('失败了',reason)
)
返回的一定是p1(成功啦),因为p1最先输出。
可以通过Promise.race([...])数组中的顺序,来动态实现先执行谁,再执行谁。
then方法是可以链式的:
Promise.race([p1,p2]).then(
value=>...
reason=>...
)
改变Promise实例状态
如何改变一个Promise实例的状态?
1.执行resolve(value):如果当前是pending就会变为fulfilled。
2.执行reject(reason):如果当前是pending就会变成rejected。
3.执行器函数(executor)抛出异常:如果当前是pending就会变为rejected。
const p = new Promise((resolve,reject)=>{
//没定义,就直接用,这里是一个异常
//console.log(a) 引擎抛异常
//throw 900 编码抛异常
})
p.then(
value=>console.log(value)
reason=>console.log(reason)
)
此时就会走异常:reason,尽管我没写reject。
改变状态与指定回调的顺序
改变Promise实例的状态 和 指定回调函数 谁先谁后?
- 都有可能,正常情况下是【先指定回调、再改变状态】,但是也可以先改变状态再指定回调。
//先指定回调、后改变状态(最常见)
//状态
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
//这里是指定resolve的回调
resolve('a')
},1000)
})
//根据状态回调
p.then{
value=>
reason=>
}
- 如何【先改状态再指定回调】? 延迟一会儿再调用then()
//先出现成功的状态,然后调用成功的回调
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('a')
},1000)
})
setTimeout(()=>{
p.then(
value=>
reason=>
)
},3000)
- Promise实例什么时候才能得到数据?
如果先指定的回调,那么状态发生改变的时候,回调函数就会调用,得到数据。
如果先改变的状态,那么当指定回调时,回调函数就会调用, 得到数据。
then的链式调用
Promise实例.then()返回的是一个【新的Promise实例】,他的值和状态由什么决定?
简单表达:由then()所指定的回调函数执行的结果,来决定。
详细表达:
- 如果then所指定的回调,返回的是非Promise值a 那么【新Promise实例】状态为:成功(fulfilled),成功的value是a。
- 如果then所指定的回调,返回的是一个Promise实例p 那么【新Promise实例】的状态、值,都与p一致。
- 如果then所指定的回调抛出异常: 那么【新Promise实例】状态为rejected,reason为抛出的那个异常。
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('a')
},1000)
})
const x = p.then(
value=>{console.log('成功啦1',value)},
reason=>{console.log('失败啦1',reason)}
)
x.then(
value=>{console.log('成功啦2',value)}
reason=>{console.log('失败啦2',reason)}
)
最后的输出结果:成功啦1,a;成功啦2,undefined
因为then指定的回调,返回的是回调的return值,在成功1那里只是console.log,没有return,所以返回值是undefined,因此x的值就是undefined。
如果then的返回值:是一个【非Promise】值,那么返回的结果就是true,就走成功的调用。
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('a')
},1000)
})
p.then(
value=>{console.log('成功啦1',value)},
reason=>{console.log('失败啦1',reason)}
).then(
value=>{console.log('成功啦2',value)}
reason=>{console.log('失败啦2',reason)}
)
因为reject,所以走失败啦1,又因为失败啦1,a语句的返回值是undefined,程序员没有手动return,是一个【非Promise值】因此表示true,后面的then走的就是true的路线,输出成功啦2,undefined。
上方这种写法就是then的链式调用。
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('a')
},1000)
})
p.then(
value=>{console.log('成功啦1',value);return Promise.reject('a')},
reason=>{console.log('失败啦1',reason)}
).then(
value=>{console.log('成功啦2',value);return true},
reason=>{console.log('失败啦2',reason);return 100}
).then(
value=>{console.log('成功啦3',value);return false},
reason=>{console.log('失败啦3',reason);return false}
).then(
value=>{console.log('成功啦4',value);return -100},
reason=>{console.log('失败啦4',reason)}
)
成功了1,a
失败了2,a
成功了3,100
成功了4,false
如果抛了异常,那么走的【失败的路线】,并且将异常的值作为失败路线的值。
p.then(
value=>{console.log('成功啦1',value);return Promise.reject('a')},
reason=>{console.log('失败啦1',reason)}
).then(
value=>{console.log('成功啦2',value);return true},
reason=>{console.log('失败啦2',reason);return 100}
).then(
value=>{console.log('成功啦3',value);return false},
reason=>{console.log('失败啦3',reason);throw 900}
).then(
value=>{console.log('成功啦4',value);return -100},
reason=>{console.log('失败啦4',reason)}
)
成功了1,a
失败了2,a
成功了3,100
失败啦4,900
总结:如果返回值是一个【非Promise】值,那么走的一定是resolve的路线,如果返回值是一个【Promise】值,根据具体的返回结果判断走resolve回调还是reject的回调。
纯回调引发的回调地狱
存在异步任务的代码,不能保证能按照顺序执行,那如果我们非要代码顺序执行呢?
setTimeout(function () { //第一层
console.log('武林要以和为贵');
setTimeout(function () { //第二程
console.log('要讲武德');
setTimeout(function () { //第三层
console.log('不要搞窝里斗');
}, 1000)
}, 2000)
}, 3000)
需要将第二次的请求,放在第一次的请求里面,这样才能保证顺序执行正确。
可以看到,代码中的回调函数套回调函数,居然套了3层,这种回调函数中嵌套回调函数的情况就叫做回调地狱。
总结:回调地狱就是为是实现代码顺序执行而出现的一种操作,它会造成我们的代码可读性非常差,后期不好维护。
解决回调地狱
学习Promise的目的就行可以用来封装一个异步操作,并且可以获取成功和失败的值。
function sendAjax(url,data){
return new Promise((resolve,reject)=>{
const xhr = new XMLHttpRequest
xhr.onreadystatechange = ()=>{
if(xhr.readyState === 4){
if(xhr.status>=200&&xhr.status<300){
resolve(xhr.response)
}else{
reject('出错了')
}
}
}
//整理参数
let str = ''
for(let key in data){
str += `${key}=${data[key]}&`
}
str = str.slice(0,-1)
xhr.open('GET',url+'?'+str)
xhr.responseType = 'json'
xhr.send()
})
}
//发送第1次请求
sendAjax('url地址',{page:1,count:2}).then(
value=>{
console.log('第1次请求成功',value);
//发送第2次请求
return sendAjax('url地址',{page:1})
},
reason=>{
console.log('第1次请求失败',reason)
}
).then(
value=>{
console.log('第2次请求成功',value)
//发送第3次请求
return sendAjax('url地址',{page:1})
},
reason=>{
console.log('第2次请求失败',reason)
}
).then(
value=>{
console.log('第3次请求成功',value)
},
reason=>{
console.log('第3次请求失败',reason)
}
)
中断Promise链
如果不中断Promise链,就会按照Promise的原则进行输出:
第一次请求出错,就会调用reason,因为reason的返回值没写,就是undefined,会直接走下一次正确的回调,所以第二次then会执行正确的回调,但是因为没有返回值,所以value输出undefined。因为第二次的请求是正确的,所以会执行第三次then,第三次正确会执行他的value,并且因为第二次是正确的,有返回值,所以只有第三次的返回内容是最全面的。
我们不希望看到上面的情况,如果我第一次发送失败了,我第二次的发送,我就不想让他发送了,所以需要中断Promise链。
.then(
value=>{...},
reason=>{
console.log('第1次请求失败了',reason);
//中断Promise链
return new Promise(()=>{})
}
).then(
value=>{
console.log('第2次请求不到了',reason);
},
reason=>{
console.log('第2次请求不到了',reason);
}
)
此时如果我第一次请求失败,我会走reason,通过失败回调函数中的return new Promise(()=>{})来中断Promise链,因为这句话既不算成功、也不算失败,【他是一个初始化pendding状态的Promise实例】,所以就中断了,第2次的then就不会执行到啦。
总结:当使用Promise的then链式调用时,在中间中断,不再调用后面的回调函数,就可以在【失败的回调函数中】返回一个pendding状态的Promise实例。
错误的穿透
Promise的错误穿透:
1.当使用promise的then链式调用时,可以在最后,用catch指定一个失败的回调。
2.前面任何操作,一旦出了问题,都会传入到最后失败的回调中处理。不过不存在then的链式调用,就不需要考虑then的错误穿透。
我们再也不用写N多个失败的回调啦,直接写一个catch就完事了!!!这就是错误的穿透!!!
sendAjax('url地址',{page:1}).then(
value=>{}
).then(
value=>{}
).then(
value=>{}
).catch(
reason=>{
console.log('失败了',reason)
}
)
底层是给每个失败补了一句:reason=>{throw reason},不处理这个错误,一直往外抛、但是到了最后一句catch就可以捕获到,最终输出错误。
Promise的优势
-
指定回调函数的方式更加灵活 旧的写法:必须在启动异步任务前指定
Promise:启动异步任务、返回Promise对象、给Promise对象绑定回调函数(甚至可以在异步任务结束后指定) -
支持链式调用,可以解决回调地狱的问题 回调地狱:回调函数嵌套使用,外部回调函数异步执行的结果是嵌套的回调函数执行的条件,使得代码不便于阅读、不便于异常的处理。
但是then的链式调用仍然不是一个优秀的解决方案,终极解决方案:async/await(底层实际上依然用的是then的链式调用)
async和await
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('a')
},1000)
})
p.then(
value =>{console.log('成功了',value)}
reason =>{console.log('失败了',reason)}
)
如果我只想拿到成功的值,就可以借助await,await:只能用来获取成功的值。
前提:await必须和async函数搭配使用
async function demo(){
const result = await p
console.log(result)//a
}
demo()
如果reject('a'),那么await永远也获取不到。
如何获取错误的值?
const p = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('a')
},1000)
})
async function demo(){
try{
const result = await p
console.log(result)
}catch(error){
console.log(error)//a
}
}
通过try..catch(error)中的error来获取失败的值。
简写:立即执行函数前面最好写一个; 或者 !
const p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('a')
},1000)
})
const p2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('b')
},2000)
})
const p3 = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject('b')
},2000)
})
;(async()=>{
try{
const result1 = await p1
console.log(result1)
const result2 = await p2
console.log(result2)
const result3 = await p3
console.log(result3)
}catch(error){
console.log(error)
}
})()
try中一旦出现了错误,那么剩下的代码就不会执行!
await的应用
;(aysnc()=>{
try{
const result1 = await sendAjax('url地址',{age:1});
console.log('第一次请求成功',result1)
const result2 = await sendAjax('url地址',{age:2});
console.log('第二次请求成功',result2)
const result3 = await sendAjax('url地址',{age:3});
console.log('第三次请求成功',result3)
}catch(error){
console.log(error);
}
})()
如果1出现了问题,那么在try-catch中,1后面的所有代码都不会执行。
await和async的规则
-
async修饰的函数: 函数的返回值为promise对象
Promise实例的结果由async函数执行的返回值决定 -
await表达式: await右侧的表达式一般为Promise实例对象,但也可以是其他的值
如果表达式是promise对象,await后的返回值是promise成功的值
如果表达式是其他值,直接将此值作为await的返回值 -
注意: await必须写在async函数中,但async函数中可以没有await
如果await的promise失败了,就会抛出异常,需要通过try...catch来捕获处理
async function demo(){
const result = await p
console.log(result)
console.log(100)
console.log(200)
}
demo()
console.log(1)
实际上先输出了最后的1,再输出了result、100、200.
因为await的原理:本质上还是一个then回调函数
async function demo(){
//const result = await p
p.then(
result=>{
console.log(result)
console.log(100)
console.log(200)
}
)
}
宏任务与微任务
宏任务与微任务,又可以说是:宏队列与微队列。
队列可以分为【宏队列】和【微队列】。【宏队列】中有宏任务,【微队列】中有微任务。
定时器是宏任务,放在宏队列中。
Promise的回调是微任务,放在微任务中。
在队列中,先执行【微队列】,再执行【宏队列】。
setTimeout(()=>{
console.log('timeout')
})
Promise.resolve(1).then(
value => console.log('成功1')
)
Promise.resolve(2).then(
value => console.log('成功2')
)
输出:成功1、成功2、timeout;先执行微队列,再执行宏队列。
微任务队列:有Promise的then回调、MutationObserver的API(很少用);
宏任务队列:定时器回调、dom事件的回调、ajax事件的回调;
在执行任何的宏任务之前,都要保证微任务队列被清空。如果微任务队列不为空,那么就优先执行微任务队列中的任务(回调)
面试题
- 第一题
setTimeout(function () {
console.log("setTimeout1");
new Promise(function (resolve) {
resolve();
}).then(function () {
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then4");
});
console.log("then2");
});
});
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("then1");
});
setTimeout(function () {
console.log("setTimeout2");
});
console.log(2);
queueMicrotask(() => {
console.log("queueMicrotask1")
});
new Promise(function (resolve) {
resolve();
}).then(function () {
console.log("then3");
});
解析:
- 定时器一上来就直接被包裹在宏任务中。
- Promise的reslove或reject直接放在主线程执行,不会放在微任务/宏任务中,但是Promise.then回调是要放在微任务的。
- 回调函数放在微任务中
第一次的过程:
- 主线程:promise1,2
- 微任务:then1,queueMicrotask1,then3
- 宏任务:setTimeout1,setTimeout2
第二次的过程:
- 将微任务中的所有任务推送到主线程中
- 主线程:promise1,2,then1,queueMicrotask1,then3
- 宏任务:setTimeout1,setTimeout2
第三次的执行过程:
-
执行第一个setTimeout,一上来就是一个console.log,所以会直接放在主线程:setTimeout1。
-
第二句Promise也是一个立即执行的,不会放在任务中,但是resolve有一个回调,所以后面的一大段全部放在微任务中。
new Promise... -
因为先执行微任务的,所以后面的setTimeout2又不能被执行
-
new Promise将then4加入微任务,console.log的then2会直接放在主线程中
- 主线程:promise1,2,then1,queueMicrotask1,then3,setTimeout1,then2,then4,setTimeout2
补充:
async function bar() {
console.log("22222")
return new Promise((resolve) => {
resolve()
})
}
async function foo() {
console.log("111111")
await bar()
console.log("33333")
}
foo()
console.log("444444")
- 默认是不会执行函数的,只有调用的时候才会。
- 因为foo是一个异步的,但是调用异步函数,实际上是会同步执行的,谁调用你,你就会直接执行。
- 只要属于同一个代码块,就会按照代码块顺序执行。
- 第二题
async function async1() {
console.log('async1 start')
await async2();
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
async1();
new Promise(function (resolve) {
console.log('promise1')
resolve();
}).then(function () {
console.log('promise2')
})
console.log('script end')
-
前两个函数没有被调用,就不会被执行,来到console,先执行console.log,直接放在主线程:
script start -
setTimeout是宏任务,直接丢宏任务里
-
async1,调用第一个函数,不管函数是同步还是异步,只要被调用,就是立马塞进主线程里的,直接输出
async1 start,然后语句块调用async2,输出async2。 -
因为用的是await,相当于new Promise.then,后面输出的
async1 end没有返回值,因此Promise是undefined,会丢到微任务中。- 此时主线程:script、async1、async2,微任务有:async1 end;宏任务:setTimeout
-
执行下面的new Promise,直接将promise1输出主线程,然后将后面的then中的promise2放入微任务中。
-
scrpit end直接输出
- 主线程:script、async1、async2、promise1、end;微任务:async1 end、promise2,宏任务:setTimeout
最后的输出结果:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
- 第三题
Promise.resolve().then(() => {
console.log(0);
return Promise.resolve(4)
}).then((res) => {
console.log(res)
})
Promise.resolve().then(() => {
console.log(1);
}).then(() => {
console.log(2);
}).then(() => {
console.log(3);
}).then(() => {
console.log(5);
}).then(() =>{
console.log(6);
})