1、同步+promise
var promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
console.log(2)
})
promise.then(()=>{
console.log(3)
})
console.log(4)
// 1
// 2
// 4
// 3
解析:
- 首先明确,
Promise构造函数是同步执行的,then方法是异步执行的 - 开始
new Promise,执行构造函数同步代码,输出1 - 再
resolve(), 将promise的状态改为了resolved,并且将resolve值保存下来,此处没有传值 - 执行构造函数同步代码,输出
2 - 跳出
promise,往下执行,碰到promise.then这个微任务,将其加入微任务队列 - 执行同步代码,输出
4 - 此时宏任务执行完毕,开始检查微任务队列,执行
promise.then微任务,输出3
2、同步+promise
var promise = new Promise((resolve, reject) => {
console.log(1)
})
promise.then(()=>{
console.log(2)
})
console.log(3)
// 1
// 3
解析:
- 开始
new Promise,执行构造函数同步代码,输出1 - 再
promise.then,因为promise中并没有resolve,所以then方法不会执行 - 执行同步代码,输出
3
3、then参数是非函数直接执行
var promise = new Promise((resolve, reject) => {
console.log(1)
})
promise.then(console.log(2))
console.log(3)
// 1
// 2
// 3
解析:
- 首先明确,
.then或者.catch的参数期望是函数,传入非函数则会发生值透传(value => value) - 开始
new Promise,执行构造函数同步代码,输出1 - 然后
then()的参数是一个console.log(2)(注意:并不是一个函数),是立即执行的,输出2 - 执行同步代码,输出
3
4、then参数是非函数值穿透
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
// 1
解析:
then(2)、then(Promise.resolve(3))发生了值穿透,直接执行最后一个then,输出1
5、宏任务+微任务
var promise = new Promise((resolve, reject) => {
console.log(1)
resolve()
reject()
})
promise.then(()=>{
console.log(2)
}).catch(()=>{
console.log(3)
})
console.log(4)
// 1
// 4
// 2
解析:
- 开始
new Promise,执行构造函数同步代码,输出1 - 再
resolve(), 将promise的状态改为了resolved,并且将resolve值保存下来,此处没有传值 - 再
reject(),此时promise的状态已经改为了resolved,不能再重新翻转(状态转变只能是pending —> resolved 或者 pending —> rejected,状态转变不可逆) - 跳出
promise,往下执行,碰到promise.then这个微任务,将其加入微任务队列 - 往下执行,碰到
promise.catch这个微任务,此时promise的状态为resolved(非rejected),忽略catch方法 - 执行同步代码,输出
4 - 此时宏任务执行完毕,开始检查微任务队列,执行
promise.then微任务,输出2
6、then函数直接return
Promise.resolve(1)
.then(res => {
console.log(res);
return 2;
})
.catch(err => {
return 3;
})
.then(res => {
console.log(res);
});
// 1
// 2
解析:
- 首先
resolve(1), 状态改为了resolved,并且将resolve值保存下来 - 执行
console.log(res)输出1 - 返回
return 2实际上是包装成了resolve(2) - 状态为
resolved,catch方法被忽略 - 最后
then,输出2
7、promise+异步
setTimeout(() => {
console.log(1)
})
Promise.resolve().then(() => {
console.log(2)
})
console.log(3)
// 3
// 2
// 1
解析:
- 首先
setTimout被放入宏任务队列 - 再
Promise.resolve().then,then方法被放入微任务队列 - 执行同步代码,输出
3 - 此时宏任务执行完毕,开始检查微任务队列,执行
then微任务,输出2 - 微任务队列执行完毕,检查执行一个宏任务
- 发现
setTimeout宏任务,执行输出1
8、resolve/reject+异步
var promise = new Promise((resolve, reject) => {
console.log(1)
setTimeout(() => {
console.log(2)
resolve()
}, 1000)
})
promise.then(() => {
console.log(3)
})
promise.then(() => {
console.log(4)
})
console.log(5)
// 1
// 5
// 2
// 3
// 4
解析:
- 首先明确,当遇到
promise.then时,如果当前的Promise还处于pending状态,我们并不能确定调用resolved还是rejected,只有等待promise的状态确定后,再做处理,所以我们需要把我们的两种情况的处理逻辑做成callback放入promise的回调数组内,当promise状态翻转为resolved时,才将之前的promise.then推入微任务队列 - 开始,
Promise构造函数同步执行,输出1,执行setTimeout - 将
setTimeout加入到宏任务队列中 - 然后,第一个
promise.then放入promise的回调数组内 - 第二个
promise.then放入promise的回调数组内 - 执行同步代码,输出
5 - 检查微任务队列,为空
- 检查宏任务队列,执行
setTimeout宏任务,输入2,执行resolve,promise状态翻转为resolved,将之前的promise.then推入微任务队列 setTimeout宏任务出队,检查微任务队列- 执行第一个微任务,输出
3 - 执行第二个微任务,输出
4
9、promise+event loop
var date = new Date()
console.log(1, new Date() - date)
setTimeout(() => {
console.log(2, new Date() - date)
}, 500)
Promise.resolve().then(console.log(3, new Date() - date))
while(new Date() - date < 1000) {}
console.log(4, new Date() - date)
答案:
1 0
3 1
4 1000
2 1000
解析:
- 首先执行同步代码,输出
1 0 - 遇到
setTimeout,定时500ms后执行,此时,将setTimeout交给异步线程,主线程继续执行下一步,异步线程执行setTimeout - 主线程执行
Promise.resolve().then,.then的参数不是函数,直接执行(value => value) ,输出3 1 - 主线程继续执行同步任务
whlie,等待1000ms,在此期间,setTimeout定时500ms完成,异步线程将setTimeout回调事件放入宏任务队列中 - 继续执行下一步,输出
4 1000 - 检查微任务队列,为空
- 检查宏任务队列,执行
setTimeout宏任务,输入2 1000
10、promise+event loop
const first = () =>
new Promise((resolve, reject) => {
console.log(3)
let p = new Promise((resolve, reject) => {
console.log(7)
setTimeout(() => {
console.log(5)
resolve(6)
}, 0)
resolve(1)
})
resolve(2)
p.then((arg) => {
console.log(arg)
})
})
first().then((arg) => {
console.log(arg)
})
console.log(4)
// => 3\
// => 7\
// => 4\
// => 1\
// => 2\
// => 5
解析:
- 主
script,new Promise立即执行,输出3 - 执行
p这个new Promise操作,输出7 - 执行
setTimeout时,因为它是宏任务,所以将回调函数放入下一轮任务队列 - 执行
resolve(1),则p的then,暂且命名为then1,放入微任务队列 - 执行
resolve(2),则first也有then,命名为then2,放入微任务队 - 执行
console.log(4),输出4,宏任务执行结束 - 再执行微任务,执行
then1,输出1,执行then2,输出2 - 第一轮事件循环结束,开始执行第二轮
- 第二轮事件循环先执行宏任务里面的,也就是
setTimeout的回调,输出5 resolve(6)不会生效,因为p的Promise状态一旦改变就不会再变化了。
11、.then、.catch多次调用
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('once')
resolve('success')
}, 1000)
})
const start = Date.now()
promise.then((res) => {
console.log(res, Date.now() - start)
})
promise.then((res) => {
console.log(res, Date.now() - start)
})
运行结果:
once
success 1005
success 1007
解析:
promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次。或者说 promise 内部状态一经改变,并且有了一个值,那么后续每次调用 .then 或者 .catch 都会直接拿到该值。
12、.then .catch return error对象
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
运行结果:
then: Error: error!!!
at <anonymous>
解析:
-
.then或者.catch中return一个error对象并不会抛出错误,所以不会被后续的.catch捕获,需要改成其中一种:- return Promise.reject(new Error('error!!!'))
- throw new Error('error!!!')
-
因为返回任意一个
非 promise 的值都会被包裹成promise 对象,即return new Error('error!!!')等价于return Promise.resolve(new Error('error!!!'))。
13、.then .catch 函数返回本身
const promise = Promise.resolve()
.then(() => {
return promise
})
promise.catch(console.error)
解析:.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环
运行结果:
14、错误捕获
Promise.resolve()
.then(function success (res) {
throw new Error('error')
}, function fail1 (e) {
console.error('fail1: ', e)
})
.catch(function fail2 (e) {
console.error('fail2: ', e)
})
运行结果:
解析:
.then可以接收两个参数,第一个是处理成功的函数,第二个是处理错误的函数.catch是.then第二个参数的简便写法.then的第二个处理错误的函数捕获不了第一个处理成功的函数抛出的错误,而后续的.catch可以捕获之前的错误
15、宏任务+微任务
process.nextTick(() => {
console.log('nextTick')
})
Promise.resolve()
.then(() => {
console.log('then')
})
setImmediate(() => {
console.log('setImmediate')
})
console.log('end')
运行结果:
end
nextTick
then
setImmediate
解析:
process.nextTick 和 promise.then都属于microtask,进入微任务队列setImmediate属于macrotask,进入宏任务队列- 第一次时,主线程(宏任务)输出
end, - 此时,宏任务执行完成,开始检查微任务,有
process.nextTick 和 promise.then两个微任务,执行输出nextTick then - 此时,第一次循环结束,开始进入下次循环,执行宏任务
setImmediate,输出setImmediate
16、交替重复亮灯
红灯3秒亮一次,绿灯1秒亮一次,黄灯2秒亮一次;如何使用Promise让三个灯不断交替重复亮灯?(海康威视笔试题)
分析:
先看题目,题目要求红灯亮过后,绿灯才能亮,绿灯亮过后,黄灯才能亮,黄灯亮过后,红灯才能亮……所以怎么通过Promise实现?
换句话说,就是红灯亮起时,承诺2s秒后亮绿灯,绿灯亮起时承诺1s后亮黄灯,黄灯亮起时,承诺3s后亮红灯……这显然是一个Promise链式调用,看到这里你心里或许就有思路了,我们需要将我们的每一个亮灯动作写在then()方法中,同时返回一个新的Promise,并将其状态由pending设置为fulfilled,允许下一盏灯亮起。
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
let myLight = (timer, cb) => {
return new Promise((resolve) => {
setTimeout(() => {
cb();
resolve();
}, timer);
});
};
let myStep = () => {
Promise.resolve().then(() => {
return myLight(3000, red);
}).then(() => {
return myLight(2000, green);
}).then(()=>{
return myLight(1000, yellow);
}).then(()=>{
myStep();
})
};
myStep();
17、封装一个异步加载图片的方法
function loadImageAsync(url) {
return new Promise(function (resolve, reject) {
var image = new Image()
image.onload = function () {
resolve(image)
}
image.onerror = function () {
reject(new Error("Could not load image at" + url))
}
image.src = url
})
}
17、promise+async/await
async function async1() {
console.log(1);
const result = await async2();
console.log(3);
}
async function async2() {
console.log(2);
}
Promise.resolve().then(() => {
console.log(4);
});
setTimeout(() => {
console.log(5);
});
async1();
console.log(6);
输出结果:1,2,6,4,3,5
分析:
Promise.resolve()的.then为then1,,加入微任务队列setTimeout加入宏任务async1()函数调用时,会输出1- 执行
await后面的函数async1(),输出2 await后的代码,相当于.then,假设为then2,加入微任务队列
- 执行
- 执行
console.log(6);,此时,宏任务执行完成,检查是否有微任务 - 执行
then1,then2,输出4,3 - 检查微任务执行完成,执行下一轮宏任务,执行
setTimeout
总结
Promise构造函数是同步执行的,then方法是异步执行的.then或者.catch的参数期望是函数,传入非函数则会直接执行Promise的状态一经改变就不能再改变,构造函数中的resolve或reject只有第一次执行有效,多次调用没有任何作用.then方法是能接收两个参数的,第一个是处理成功的函数,第二个是处理失败的函数,再某些时候你可以认为catch是.then第二个参数的简便写法- 当遇到
promise.then时, 如果当前的Promise还处于pending状态,我们并不能确定调用resolved还是rejected,只有等待promise的状态确定后,再做处理,所以我们需要把我们的两种情况的处理逻辑做成callback放入promise的回调数组内,当promise状态翻转为resolved时,才将之前的promise.then推入微任务队列 promise的.then或者.catch可以被调用多次,promise内部状态一经改变,并且有了一个值,那么后续每次调用.then或者.catch都会直接拿到该值。
习题汇总自: