系列文章目录
好多知识点
一、单线程和异步
- JS是单线程语言
- 浏览器和 nodejs 已支持JS启动进程,如 Web Worker
- JS 和 DOM 渲染共用同一个线程,因为 JS 可修改 DOM 结构
- 遇到等待(网络请求,定时任务)不能卡住
- 需要异步
- 回调 callback 形式调用
异步和同步
- 基于 JS 是单线程语言
- 异步不会阻塞代码执行
- 同步会阻塞代码执行
异步应用场景
- 网络请求,如 ajax 图片加载
- 定时任务,如 setTimeout
二、Promise
const url1 = '/data1.json';
const url2 = '/data2.json';
const url3 = '/data3.json';
getData(url1).then(data1 => {
console.log(data1);
return getData(url2);
}).then(data2 => {
console.log(data2);
return getData(url3);
}).then(data3 => {
console.log(data3);
}).catch(err => console.log(err));
function getData(url) {
return new Promise((resolve, reject) => {
$.ajax({
url,
success(data) {
resolve(data)
},
error(err) {
reject(err)
}
})
}
)
}
三种状态
- pending resolved rejected
- pending->resolved或pending->rejected
- 变化不可逆
状态的表现和变化
- pending状态,不会触发then和catch
- resolved状态,会触发后续的then回调函数
- rejected状态,会触发后续的catch回调函数
then和catch对状态的影响
- then正常返回resolved,里面有报错则返回rejected
- catch正常返回resolved,里面有报错则返回rejected
// catch正常返回resolved情况
const p1 = Promise.reject('1').catch(err => {
console.error(err)
})
console.log('p1',p1); // resolved 注意! 触发 then 回调
p1.then(() => {
console.log(11)
})
const p2 = Promise.reject('2').catch(err => {
throw new Error('catch err')
})
console.log('p2',p2); // rejected 触发 catch 回调
p2.then(() => {
console.log(2)
}).catch(() => {
console.log('打印')
})
手写Promise
- 初始化 & 异步调用
- then catch 链式调用
- API .resolve .reject .all .race
const p1 = new MyPromise((resolve,reject) => {
resolve(100);
// serTimeout(() => {
// resolve(100)
// },500)
})
const p11 = p1.then(data => {
return data + 1;
})
const p12 = p11.then(data => {
return data + 2;
})
const p13 = p12.catch(err => console.error(err));
const p2 = MyPromise.resolve(200);
const p3 = MyPromise.reject('err..');
const p4 = MyPromise.all([p1,p2]);
const p5 = MyPromise.race([p1,p2]);
class MyPromise {
state = 'pending'; // 状态,'pending' 'fulfilled' 'rejected'
value = undefined; // 成功后的值
reason = undefined; // 失败后的原因
resolveCallbacks = []; // pending 状态下,存储成功的回调
rejectCallbacks = []; // pending 状态下,存储失败的回调
constructor(fn) {
const resolveHandler = (value) => {
if(this.state === 'pending') {
this.state = 'fulfilled';
this.value = value;
this.resolveCallbacks.forEach(fn => fn(this.value));
}
}
const rejectHandler = (reason) => {
if(this.state === 'pending') {
this.state = 'rejected';
this.reason = reason;
this.rejectCallbacks.forEach(fn => fn(this.reason));
}
}
try {
fn(resolveHandler,rejectHandler)
} catch (err) {
rejectHandler(err)
}
}
then (fn1,fn2) {
// 当 pending 状态下, fn1 fn2会被存储到 callbacks 中
fn1 = typeof fn1 === 'function' ? fn1 : (v) => v;
fn2 = typeof fn2 === 'function' ? fn2 : (e) => e;
if(this.state === 'pending') {
const p1 = new MyPromise((resolve,reject) => {
this.resolveCallbacks.push(() => {
try {
const newValue = fn1(this.value);
resolve(newValue);
} catch (err) {
reject(err)
}
})
this.rejectCallbacks.push(() => {
try {
const newReason = fn2(this.reason);
reject(newReason);
} catch (err) {
reject(err)
}
})
})
}
if(this.state === 'fulfilled') {
const p1 = new MyPromise((resolve,reject) => {
try {
const newValue = fn1(this.value);
resolve(newValue);
} catch (err) {
reject(err);
}
})
return p1;
}
if(this.state === 'rejected') {
const p1 = new MyPromise((resolve,reject) => {
try {
const newReason = fn2(this.reason);
reject(newReason);
} catch (err) {
reject(err);
}
})
return p1;
}
}
// 就是then的一个语法糖,简单模式
catch (fn) {
return this.then(null,fn);
}
}
MyPromise.resolve = function(value){
return new MyPromise((resolve,reject) => resolve(value));
}
MyPromise.reject = function(reason){
return new MyPromise((resolve,reject) => reject(reason));
}
// all传入promise数组,等待所有都fulfill之后,返回新promise,包含前面所有的结果
MyPromise.all = function (promiseList = []){
const p1 = new MyPromise((resolve,reject) => {
const result = []; // 存储promiseList所有信息
const length = promiseList.length;
let resolvedCount = 0;
promiseList.forEach(p => {
p.then(data => {
result.push(data);
// resolvedCount必须在then里面++
// 不能用index
resolvedCount++;
if(resolvedCount === length){
// 已经遍历到最后一个promise
resolve(result);
}
}).catch(err => {
reject(err);
})
})
})
return p1;
}
// race传入promise数字,只要有一个fulfill即可返回
MyPromise.race = function (promiseList = []){
let resolved = false; // 标记
const p1 = new MyPromise((resolve,reject) => {
promiseList.forEach(p => {
p.then(data => {
if(!resolved) {
resolve(data)
resolved = true
}
}).catch((err) => {
reject(err);
})
})
})
return p1;
}
三、event loop(事件循环/事件轮询)
- JS是单线程运行的
- 异步要基于回调来实现
- event loop就是异步回调的实现原理
- DOM事件也是使用回调,基于event loop
event loop过程
- 同步代码,一行行放在call stack执行
- 遇到异步,会先“记录”下,等待时机(定时、网络请求等)
- 时机到了,就移动到callback queue
- 如call stack为空(即同步代码执行完)event loop开始工作
- 轮询查找callback queue,如有则移动到call stack执行
- 然后继续轮询查找
四、async/await
- 执行async函数,返回的是Promise对象
- await相当于Promise的then
- try...catch 可捕获异常,代替了Promise的catch
五、宏任务 macroTask 和 微任务 microTask
- 宏任务:setTimeout,setInterval,Ajax,DOM事件
- 微任务:Promise,async/await
- 微任务执行时机比宏任务要早
event loop和DOM渲染
- 每次Call Stack清空(即每次轮询结束),即同步任务执行完
- 都是DOM重新渲染的机会,DOM结果如有改变则重新渲染
- 然后再去触发下一次Event Loop
微任务和宏任务的区别
- 宏任务:DOM渲染后触发,如setTimeout
- 微任务:DOM渲染前触发,如Promise
总结
JS异步知识笔记