promise出现的原因
- 异步执行
- 循环执行 + 循环终止(时延的存在)
计算机原理
进程和线程的概念和区别:
- 进程:是CPU资源分配的最小单位,享有独立资源,可协同多个线程来完成上级CPU的命令。即,进程执行CPU命令时,是分配给多个线程去执行任务的。
- 线程:是CPU调度的最小单位,与其他线程一起共享各个进程的资源,多个线程合作完成进程的命令。
面试题:
- Chrome新开一个窗口,新开的tab页是属于进程还是线程?属于一个进程,可以从进程的概念开始说起。
浏览器开启了多个TAB页,多个TAB页是多个进程,一个个TAB页去分配多个线程执行任务。
CPU分配给进程资源时,应用根据需要开启线程。 - 窗口(进程间)通信通过什么?浏览器的存储storage、cookie。=>可能问到多种存储的区别。=>可能问到浏览器的原理。
浏览器原理
- GUI渲染引擎:
- 任务:完善页面骨架。解析HTML、CSS,构建DOM树,然后进行布局、绘制。
- 特点:与JS引擎互斥,当执行JS引擎线程时,GUI会pending(挂起),当任务队列空闲时,才会继续执行GUI。
- JS引擎:
- 任务:处理JS,解析执行脚本;分配、处理、执行待执行的事件,使用event队列。
- 特点:阻塞GUI渲染。
- 定时器触发引擎:
- 任务:异步定时器处理与执行。接收JS引擎分配的定时器任务并计数。处理完成后交予事件触发线程触发。
- 异步HTTP请求线程:
- 任务:异步执行请求类处理(Promise/ajax等)。接收JS引擎分配的异步HTTP请求。监听回调,交予事件触发线程触发。
- 事件触发引擎:
- 接收来源:定时器、异步、用户操作。
- 任务:将回调过来的事件依次接入到任务队列的队尾,然后还给JS引擎。
JS执行原理(浏览器工作流程)
- Memory Heap(存储堆):分配内存。相当于一个仓库,存储所有代码。
- Call Stack():执行回调。存储所有即将要执行的代码。
与WebAPI进行交互。
JS任务分类:
纵向分类:(优先级:宏 > 微,有微则微,无微则宏)
- 宏任务macro:script、setTimeout、setInterval、I/O。
- 微任务micro:new Promise(){}.then()、webAPI。
横向分类:
- 同步:直接进入主线程
- 异步:先进入event table、event queue,等主线程为空再执行。
面试题:
- Event Loop问题:JS是单线程预约,单步执行。JS堆栈的执行顺序与堆栈溢出 => 性能优化。执行顺序题。
- 写一个异步请求函数。
request.onreadystatechange = () => {
if(request.readyState === 4) {
const _status = request.status;
if(_status === 200) {
const _res = request.responseText;
return success(_res);
}else {
return fail(_status);
}
}
}
Promise规范及应用
Promise出现的原因:拯救回调导致的无穷嵌套。
Promise的特点:
- 多个异步顺序执行。=>链式调用p.then().then()…
- 全部执行完成后再操作。=>Promise.all([promise1, …]).then(res => {…})
- 一旦有执行完成的立刻操作。=>Promise.race([promise1, …]).then(res => {…})
描述Promise框架:(即Promise规范,面试常考)
- Promise的状态:pending、fulfilled、rejected
- new Promise执行器executor(),执行器参数是resolve、reject。
- promise的默认状态:pending。
- promise状态的流转:promise=>resolve,promise=>fulfilled。
- promise保存成功状态的枚举(value值):undefined、thenable、promise。
- promise保存失败状态的值(reason):reason保存失败,描述promise接口。
- promise一定会有then,then的接收来源:onFulfilled(value) + onRejected(reason)。
手写Promise
//三个状态:PENDING FULFILLED REJECTED
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class Promise {
constructor(executor) {
// 默认状态的处理: PENDING
this.status = PENDING;
// 成功状态的值
this.value = undefined;
// 失败状态的值
this.reason = undefined;
// 存放成功的回调
this.onResolvedCallbacks = []
// 存放失败的回调
this.onRejectedCallbacks = []
// 成功状态的回调
let resolve = value => {
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 依次调用对应函数的执行
(this.onResolvedCallbacks || []).forEach(fn => fn())
}
};
// 失败状态的回调
let reject = reason => {
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 依次调用对应函数的执行
(this.onRejectedCallbacks || []).forEach(fn => fn())
}
}
try {
executor(resolve, reject);
}catch(error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
if(this.status === FULFILLED) {
onFulfilled(this.value)
}
if(this.status === REJECTED) {
onRejected(this.reason)
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() =>{
onFulfilled(this.value)
})
this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
}
}
}
async await
比promise更进一步解决回调的问题,像是把异步代码按照同步的方式来写,执行时才异步。如下,promise方式还是会出现相互嵌套的问题,但是async await则不会,看起来更为简便清晰。
// promise方式
function wait500(input) {
return new Promise((resolve, reject) => {
console.log('wait500', input)
setTimeout(() => {
resolve(input + 500)
}, 500);
});
}
wait500.then(res => {
wait500.then(res => {
wait500.then(res => {
wait500.then(res => {
})
})
})
})
// async & await方式
async function asyncCall() {
const result = await wait500(0);
result = await wait500(0);
result = await wait500(0);
result = await wait500(0);
console.log('asyncCall', result);
}
asyncCall();
generator
使用步进的方式代替then。例如当Ajax不知道需要执行多少时间时,使用generator方式会比promise更为准确。
function * generator() {
let index = 0;
while(true)
yield index++;
}
let gen = generator();
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
面试重点:
结合流水线对generator做自动化处理。
const GEN_LINE = [1,2,3,4,5,6];
(GEN_LINE || []).forEach(i => {
console.log(gen.next(i).value);
})