题目一
async function async1() {
console.log('async1 start');
try {
async2()
} catch (e) {
console.log(`error: ${e.message}`);
}
}
function async2() {
console.log('async2');
async3();
}
async function async3() {
throw new Error('async3 error');
}
console.log('script start');
setTimeout(function () {
console.log('setTimeout');
}, 0)
requestAnimationFrame(function () {
console.log('animation frame');
})
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end');
这个题目中,我说错了两个地方
- async3 函数中 throw 出来的 error 是不会被捕获的,因为没有 await。想要错误被捕获有两种修改方式 方法一: 将 async3 改成普通函数,那么普通函数抛出的错误是会捕获的
async function async1() {
console.log('async1 start');
try {
async2()
} catch (e) {
console.log(`error: ${e.message}`);
}
}
function async2() {
console.log('async2');
async3();
}
function async3() {
throw new Error('async3 error');
}
方案二:层层 await
async function async1() {
console.log('async1 start');
try {
await async2()
} catch (e) {
console.log(`error: ${e.message}`);
}
}
async function async2() {
console.log('async2');
await async3();
}
async function async3() {
throw new Error('async3 error');
}
原因是在 promise 的 executor 和 then 的处理函数中,其实有一层隐式的 try ... catch ...,当有 throw error 或者有其它语法错误的时候就相当于 Promise.reject(error)。比如:
new Promise((resolve, reject) => {
throw new Error("Whoops!")
}).catch(alert) // Error: Whoops!
// 等价于
new Promise((resolve, reject) => {
reject(new Error("Whoops!"));
}).catch(alert) // Error: Whoops!
下面这两种也是一样的
new Promise((resolve, reject) => {
resolve("ok")
}).then((result) => {
throw new Error("Whoops!") // reject 这个 promise
}).cathc(alert); // Error: Whoops!
// 对于所有的 error,不仅仅是 throw 的 error 都会这样
new Promise((resolve, reject) => {
resolve("ok")
}).then((result) => {
blabal(); // reject 这个 promise
}).cathc(alert); // ReferenceError: blabla is not defined
- 还有就对 raf 是什么时候执行 在 B 站找到一个挺好的视频www.bilibili.com/video/BV1K4… 里面详细的讲了 eventLoop。有一个我的知识盲区,我只知道 raf 是下一次渲染之前调用的,使用它是为了不要等待太久这样一个概念。 通过这个视频,我理解了 eventLoop 会不停的从 task queue 里面获取新的 task,但是并不是一次 task 就会对应着一次渲染,有可能执行完多次 task 之后才会执行一次渲染。在题目中,如果不对页面做任何操作,那么 setTimeout 这个宏任务会先执行,然后在下一次渲染之前会执行 raf;但是执行这段代码的时候,页面还在持续滚动,可能就会先执行 raf 再执行 setTimeout。
题目二
下面这段代码在浏览器的表现
<style>
.box {
width: 500px;
height: 300px;
background-color: gray;
}
.transition {
background-color: black;
transition: background-color 2000ms ease-in-out;
}
</style>
<body></body>
<script>
const box = document.createElement('div')
box.classList.add('box')
document.body.appendChild(box)
box.classList.add('transition')
</script>
实际上是会直接展示一个黑色的盒子而且也没有动画。 原因也是在上面的视频中提到的,上面的这一段脚本相当于一个 task,在这个 task 执行完之后,box 的 class 是即有 .box 又有 .transition,所以不会出现动画,要想出现动画,可以像下面这样改造一下
<script>
const box = document.createElement('div')
box.classList.add('box')
document.body.appendChild(box)
window.requestAnimation(() => {
box.classList.add('transition')
})
</script>
题目三
手写代码题目
class RequestQueue {
maxLength: number;
lastIndex: number;
inProgressQueue: {
fn: () => Promise<any>;
resolve: (value: any | PromiseLike<any>) => void;
reject: (reason?: any) => void;
hasInvoked: boolean;
}[];
constructor(maxLength: number) {
this.maxLength = maxLength;
this.inProgressQueue = [];
this.lastIndex = -1;
}
request(func: () => Promise<any>): Promise<any> {
return new Promise((resolve, reject) => {
this.inProgressQueue.push({
fn: func,
resolve,
reject,
hasInvoked: false,
});
if (this.inProgressQueue.length <= this.maxLength) {
this.run();
}
});
}
run() {
const item = this.inProgressQueue.find(
(queueItem) => queueItem.hasInvoked === false
);
if (!item) {
return;
}
const { fn, resolve, reject } = item;
item.hasInvoked = true;
fn()
.then(
(value) => {
resolve(value);
},
(error) => {
reject(error);
}
)
.finally(() => {
this.inProgressQueue = this.inProgressQueue.filter(
(queueItem) => queueItem !== item
);
this.run();
});
}
}
const instance = new RequestQueue(3);
instance
.request(async () => {
await delay(100);
return Promise.resolve(1);
})
.then((value) => {
console.log({ result1: value });
});
instance
.request(async () => {
await delay(1000);
return Promise.resolve(2);
})
.then((value) => {
console.log({ result2: value });
});
instance
.request(async () => {
await delay(700);
return Promise.resolve(3);
})
.then((value) => {
console.log({ result3: value });
});
instance
.request(async () => {
await delay(900);
return Promise.resolve(4);
})
.then((value) => {
console.log({ result4: value });
});
function delay(time: number): Promise<void> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
export default RequestQueue;