基础知识
event loop事件循环使得JS单线程可以并发的执行异步任务而无需额外创建线程。
JS中的异步任务分为宏任务和微任务。
宏任务:包括setTimeout、setInterval、I/O、UI渲染、事件处理等
微任务:包括Promise的回调(.then, .catch)、MutationObserver、process.nextTick(Node.js特有)等。
event loop的执行顺序
1.整个方法作为一个宏任务执行
2.执行过程中同步代码直接执行,宏任务进入宏任务队列,微任务进入微任务队列
3.当前宏任务执行完成后,检查微任务列表,有就依次执行
4.执行浏览器UI线程的渲染工作
5.执行完本轮的宏任务,回到2,循环直到宏任务和微任务都为空。
promise的.then .catch .finally的相关题目
promise.then的时候如果状态不是pending就会把它加入到微任务队列中,如果还是pending状态then不会执行,需要等到被改变状态后才被加入到微任务队列中。
题目一
const promise = new Promise((resolve, reject) => {resolve("success1");reject("error");resolve("success2");
});
promise
.then(res => {console.log("then: ", res);}).catch(err => {console.log("catch: ", err);})output
"then:success1"
promise的状态一经改变不能再改变
题目二
const promise = new Promise((resolve, reject) => {reject("error");resolve("success2");
});
promise
.then(res => {console.log("then1: ", res);}).then(res => {console.log("then2: ", res);}).catch(err => {console.log("catch: ", err);}).then(res => {console.log("then3: ", res);})outout
"catch: " "error"
"then3: " undefined
catch不管被连接在哪里,都能捕获上层未捕获的错误。而then3被执行是因为catch()也返回一个promise,由于这个promise没有返回值,所以打印出来的是undefined
题目三
Promise.resolve(1).then(res => {console.log(res);return 2;}).catch(err => {return 3;}).then(res => {console.log(res);});output
1
2
promise可以链式调用,不过proomise每次调用.then或者.catch都会返回一个新的promise,从而实现链式调用。
题目四
const promise = new Promise((resolve, reject) => {setTimeout(() => {console.log('timer')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)
})output
'timer'
'success' 1001
'success' 1001
promise的.then或者.catch可以被调用多次,但是promise构造函数只执行一次,promise的内部状态一旦改变,并且有了一个值,后续每次.then或者.catch都会直接拿到这个值。
题目五
Promise.resolve().then(() => {return new Error('error!!!')
}).then(res => {console.log("then: ", res)
}).catch(err => {console.log("catch: ", err)
})output
"then: " "Error: error!!!"
返回任意一个非 promise
的值都会被包裹成 promise
对象,因此这里的return new Error('error!!!')
也被包裹成了return Promise.resolve(new Error('error!!!'))
。
如果你想抛出一个错误,可以用下面的方式
return Promise.reject(new Error('error!!!'));
// or
throw new Error('error!!!')
题目六
Promise.resolve(1).then(2).then(Promise.resolve(3)).then(console.log)output
1
.then
或者 .catch
的参数期望是函数,传入非函数则会发生值透传。第一个then
和第二个then
中传入的都不是函数,一个是数字类型,一个是对象类型,因此发生了透传,将resolve(1)
的值直接传到最后一个then
里。
题目七
Promise.reject('err!!!').then((res) => {console.log('success', res)}, (err) => {console.log('error', err)}).catch(err => {console.log('catch', err)})'error' 'error!!!'
进入的是then()里的第二个参数里面,不会进入catch,如果把第二个参数去掉就进入catch了。
Promise.resolve().then(function success (res) {throw new Error('error!!!')}, function fail1 (err) {console.log('fail1', err)}).catch(function fail2 (err) {console.log('fail2', err)})fail2 Error: error!!!
但如果是在then里面抛出异常,会被catch捕获。
题目八
Promise.resolve('1').then(res => {console.log(res)}).finally(() => {console.log('finally')})
Promise.resolve('2').finally(() => {console.log('finally2')return '我是finally2返回的值'}).then(res => {console.log('finally2后面的then函数', res)})'1'
'finally2'
'finally'
'finally2后面的then函数' '2'
这两个Promise
的.finally
都会执行,且就算finally2
返回了新的值,它后面的then()
函数接收到的结果却还是'2'。
如果在finally中抛出异常,会被catch捕获。
Promise.resolve('1').finally(() => {console.log('finally1')throw new Error('我是finally中抛出的异常')}).then(res => {console.log('finally后面的then函数', res)}).catch(err => {console.log('捕获错误', err)})'finally1'
'捕获错误' Error: 我是finally中抛出的异常
题目九
而promise的多个链式调用的执行时机又是如何的?需要等到前一个微任务执行完成后,才会继续执行后面的.then或者.finally操作。代码中标注了微任务在队列中的顺利,也就是执行到微任务1的时候,微任务3才进微任务队列,这个时候前面已经有微任务2了。
function promise1 () {let p = new Promise((resolve) => {console.log('promise1');resolve('1')})return p;
}
function promise2 () {return new Promise((resolve, reject) => {reject('error')})
}
promise1().then(res => console.log(res)) // 微任务1.catch(err => console.log(err)).finally(() => console.log('finally1')) // 微任务3promise2().then(res => console.log(res)) .catch(err => console.log(err)) // 微任务2.finally(() => console.log('finally2')) // 微任务4'promise1'
'1'
'error'
'finally1'
'finally2'
promise的all和race
.all():接收一组异步任务并行执行,并在所有异步任务执行完成后才执行回调
.race():接收一组异步任务并行执行,只取第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被丢弃。
当一组异步任务中有返回错误的情况,.all()会如何执行?
function runAsync (x) {const p = new Promise(r => setTimeout(() => r(x, console.log(x)), 1000))return p
}
function runReject (x) {const p = new Promise((res, rej) => setTimeout(() => rej(`Error: ${x}`, console.log(x)), 1000 * x))return p
}
Promise.all([runAsync(1), runReject(4), runAsync(3), runReject(2)]).then(res => console.log(res)).catch(err => console.log(err))// 1s后输出
1
3
// 2s后输出
2
Error: 2
// 4s后输出
4
all和race
传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被then
的第二个参数或者后面的catch
捕获;但并不会影响数组中其它的异步任务的执行。
race的使用场景:我们可以用race给某个异步任务设置超时事件。
all的使用场景:进入页面并发下载页面所需要的初始化资源,等结果都返回后再刷新页面。
async/await
async function async1() {console.log("async1 start");await async2();console.log("async1 end");
}
async function async2() {console.log("async2");
}
async1();
console.log('start')output
'async1 start'
'async2'
'start'
'async1 end'
await后面的语句相当于放到一个promise中,下一行以及之后的语句相当于放在promise.then中。
async function async1() {console.log("async1 start");// 原来代码// await async2();// console.log("async1 end");// 转换后代码new Promise(resolve => {console.log("async2")resolve()}).then(res => console.log("async1 end"))
}
如果async直接返回一个值,会输出什么?
async function fn () {// return await 1234// 等同于return 123
}
fn().then(res => console.log(res))123
正常是希望得到一个promise对象,如果不是就会直接返回对应的值,等价于promise.resolve()
未完待续...