异步和同步
同步
执行某个任务时,没有得到结果之前不会继续后续操作
异步
一个异步任务执行之后,没有得到结果之前就可以继续执行后续操作,异步任务完成之后,一般通过回调通知调用者;一般异步任务都会开启另一个线程
常见的异步任务:setTimeout、fetch、XMLHttpRequest
异步的解决方案
回调函数
使用setTimeout模拟请求
function login(callback) {
setTimeout(() => {
callback("token")
}, 100);
}
function getOrderId(token,callback) {
if(token){
setTimeout(() => {
callback("orderId")
}, 100);
}
}
function orderDetails(orderId,callback) {
if(orderId){
setTimeout(() => {
callback("订单详情")
}, 100);
}
}
login((token)=>{
getOrderId(token,(orderId)=>{
orderDetails(orderId,(orderInfo)=>{
console.log(orderInfo)
})
})
})
缺点:
1、形成回调地狱
2、高度耦合
3、不容易维护
4、不能直接使用return
事件驱动
基于回调函数,思想是异步任务的执行取决于事件的驱动
// 登录完成派发事件
const loginEvent = new CustomEvent("login-end",{detail:"token"})
function login() {
setTimeout(() => {
window.dispatchEvent(loginEvent)
}, 100);
}
// 监听登录完成,执行获取订单id事件,完成订单id获取之后又派发事件
const orderEvent = new CustomEvent("order-end",{detail:"orderId"})
function getOrderId(token) {
if(token){
setTimeout(() => {
window.dispatchEvent(orderEvent)
}, 100);
}
}
function tokenListener(ev) {
getOrderId(ev.detail)
}
window.addEventListener("login-end",tokenListener)
const orderDetailsEvent = new CustomEvent("orderDetails-end",{detail:"订单详情"})
function orderDetails(orderId) {
if(orderId){
setTimeout(() => {
window.dispatchEvent(orderDetailsEvent)
}, 100);
}
}
function orderListener(ev) {
orderDetails(ev.detail)
}
window.addEventListener("order-end",orderListener)
window.addEventListener("orderDetails-end",(e)=>{
console.log(e.detail)
})
login()
优点:
去耦合、便于实现模块化
缺点:
运行流程不清晰、阅读代码困难
发布订阅
也是使用事件驱动,但是有一个消息中心,可以看到消息流转
订阅中心
class MsgCenter {
constructor(){
this.listeners = {};
}
// 消息订阅
on(type,listener){
if(this.listeners[type] === undefined){
this.listeners[type] = [];
}
this.listeners[type].push(listener);
return listener;
}
// 发送
dispatch(type,args = {}){
if(!type){
throw new Error("Event object missing 'type' property")
}
if(this.listeners[type] instanceof Array){
const listeners = this.listeners[type];
for(let i=0;i<listeners.length;i++){
listeners[i].call(this,type,args)
}
}
}
// 取消订阅
off(type, listener){
if (this.listeners[type] instanceof Array) {
const listeners = this.listeners[type];
for(let i=0;i<listeners.length;i++){
if(listeners[i] === listener){
listeners[i].splice(i,1);
break;
}
}
}
}
}
消息订阅
const myMsgCenter = new MsgCenter();
function login() {
setTimeout(() => {
myMsgCenter.dispatch("login-end",{detail:"token"})
}, 100);
}
function getOrderId(token) {
if(token){
setTimeout(() => {
myMsgCenter.dispatch("order-end",{detail:"orderId"})
}, 100);
}
}
function tokenListener(type,ev) {
getOrderId(ev.detail);
myMsgCenter.off(type,tokenListener)
}
myMsgCenter.on("login-end",tokenListener)
function orderDetails(orderId) {
if(orderId){
setTimeout(() => {
myMsgCenter.dispatch("orderDetails-end",{detail:"订单详情"})
}, 100);
}
}
function orderListener(type,ev) {
orderDetails(ev.detail)
myMsgCenter.off(type,orderListener)
}
myMsgCenter.on("order-end",orderListener)
myMsgCenter.on("orderDetails-end",(e)=>{
console.log(e.detail)
})
login()
Promise
特点:拉平回调函数,把回调嵌套变为链式调用;本质其实还是回调
function login() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("token")
}, 100);
})
}
function getOrderId(token) {
return new Promise((resolve, reject) => {
if(token){
setTimeout(() => {
resolve("orderId")
}, 100);
}
})
}
function orderDetails(orderId) {
return new Promise((resolve, reject) => {
if(orderId){
setTimeout(() => {
resolve("订单详情")
}, 100);
}
})
}
login().ten(getOrderId).then(orderDetails).then(r=>console.log(r))
缺点:
代码不够简洁,每个方法都需要Promise包裹
无法取消Promise
错误需要通过回调再去捕获,会麻烦一点
Generator
特点就是可以控制函数执行;将Promise的链式调用封装一下
function* execute() {
const token = yield login()
const orderId = yield getOrderId(token)
const details = yield orderDetails(orderId)
}
let g = execute();
let {value,done} = g.next();
value.then((token)=>{
let {value,done} = g.next(token);
value.then((orderId)=>{
let {value,done} = g.next(orderId);
value.then((r)=>{
console.log(r)
})
})
})
async + await
async函数是Generator函数的语法糖;目前算是最优的一种方法
async function execute() {
const token = await login()
const orderId = await getOrderId(token)
const details = await orderDetails(orderId)
console.log(details)
}
execute()
Promise
Promise必定会有结果并且只可能有两种结果:
pendding(初始状态)——>fulfilled(成功状态)
pendding——>rejected(失败状态)
静态方法
resolve:成功状态返回的Promise对象
reject:失败状态返回的Promise对象
all:执行多个Promise对象,全部执行成功或者任意一个失败返回的Promise对象
allSettled:执行多个Promise对象,不论成功失败,结果全部返回
any:接受一个Promise集合,返回第一个成功者
race:执行多个Promise,返回最快的Promise触发结果,不管成功还是失败;race等待一个Promise执行之后,其他并不会停止,只是race有结果了
const p1 = new Promise((resolve,reject)=>{
setTimeout(() => {
console.log('p1')
resolve(1)
}, 3000);
});
const p2 = new Promise((resolve,reject)=>{
setTimeout(() => {
console.log('p2')
resolve(2)
}, 2000);
});
const p3 = new Promise((resolve,reject)=>{
setTimeout(() => {
console.log('p3')
resolve(3)
}, 1000);
});
Promise.race([p1, p2, p3]).then((r)=>{
console.log(r)
})
原型方法
prototype.then:返回一个Promise,最多两个参数,成功和失败后的回调函数
prototype.catch:返回一个Promise,处理失败的情况
prototype.finally:返回一个Promise,在Promise结束时,无论是成功或者失败都会执行
多次尝试
function isFunction(fn) {
return typeof fn === "function" || fn instanceof Function;
}
// fn要执行的函数、count执行次数、assert断言函数
function retry(fn,count,assert=()=>false) {
if(!isFunction(fn)){
return Promise.resolve(fn);
}
return new Promise(async (resolve, reject) => {
let err = null;
for(let tryCount = 1;tryCount <= count;tryCount++){
try{
const res = await fn(tryCount);
if(assert(res,tryCount)){
return resolve(res)
}
}catch(e){
err = e;
}
}
reject("多次尝试失败")
})
}
let index = 0;
function createPromise(tryCount) {
return new Promise((resolve, reject)=>{
index++;
setTimeout(() => { resolve(index)}, 1000);
})
}
retry(createPromise,10,(res)=>{
return res == 5
}).then(r=>console.log(r))
异常捕获
1、.catch
Promise.resolve("success").then((res)=>{
throw new Error("error")
}).catch((err)=>{console.log(err)});
2、监听unhandledrejection事件;异步好像是监听不到的
const p = new Promise((resolve)=>{
throw new Error("error")
resolve(1)
});
window.addEventListener('unhandledrejection', (e)=>{
console.log(e)
})
监听不到
const p = new Promise((resolve)=>{
setTimeout(() => {
throw new Error("error")
resolve(1)
}, 100);
});
window.addEventListener('unhandledrejection', (e)=>{
console.log(e)
})
3、reject影响链式调用;注意catch的执行条件,只reject了一次,所以之后执行一次
const p = new Promise((resolve,reject)=>{ reject(1)
});
p.then((success)=>{
// 不会执行
console.log(success)
},(err)=>{
console.log(err)
return err;
}).then((res)=>{
console.log(res)
}).catch((err)=>{
// 不会执行
console.log(err)
})
resolve或者reject之后代码还是会继续执行,但是resolve之后再抛出异常是不能被reject捕获的,反之也是
值穿透
const p = new Promise((resolve,reject)=>{ resolve(1)
});
p.then(2).then((r)=>{
console.log(r) // 1
})
手写Promise
class MyPromise {
constructor(callback){
this.PromiseStatus = 'pending';
this.PromiseValue = null;
// 初始化callback集合
this.initCollections();
// 绑定this指向
this.initBind();
this.executor(callback);
}
initCollections(){
this.fulfilledCallbacks = [];
this.rejectedCallbacks = [];
}
initBind(){
this.resolve = this.resolve.bind(this);
this.reject = this.reject.bind(this);
}
// 如果抛出异常,会执行reject回调
executor(callback){
try {
callback(this.resolve,this.reject)
} catch (error) {
this.reject(error)
}
}
resolve(value){
if(this.PromiseStatus !== 'pending'){
return;
}
this.PromiseStatus = 'fulfilled';
this.PromiseValue = value;
while (this.fulfilledCallbacks.length) {
this.fulfilledCallbacks.shift()(this.PromiseValue);
}
}
reject(value){
if(this.PromiseStatus !== 'pending'){
return;
}
this.PromiseStatus = 'rejected';
this.PromiseValue = value;
while (this.rejectedCallbacks.length) {
this.rejectedCallbacks.shift()(this.PromiseValue);
}
}
then(onFulfilled, onRejected){
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : ()=>{};
onRejected = typeof onRejected === 'function' ? onRejected : ()=>{};
const nextPromise = new MyPromise((resolve, reject)=>{
const handlePromise = (fn)=>{
setTimeout(() => {
try {
// 执行回调,取得返回的内容
const res = fn(this.PromiseValue);
if(res === nextPromise){
throw new Error("不能返回自身")
}
if(res instanceof MyPromise){
res.then(resolve,reject)
}else {
resolve(res);
}
} catch (error) {
reject(error)
}
});
}
if(this.PromiseStatus === 'fulfilled'){
handlePromise(onFulfilled);
}else if(this.PromiseStatus === 'rejected'){
handlePromise(onRejected);
}else {
this.fulfilledCallbacks.push(handlePromise.bind(this,onFulfilled))
this.rejectedCallbacks.push(handlePromise.bind(this,onRejected))
}
})
return nextPromise;
}
}
const p = new MyPromise((resolve,reject)=>{
resolve(10)
})
p.then((r)=>{
return r* 10
}).then((r)=>{
console.log(r)
})
console.log(2)
async+await
generator函数(生成器函数)
1、本质是一个普通函数,function关键字和函数名之间有一个星号
function* generator1() {} // 推荐写法
function * generator1() {}
function *generator1() {}
function*generator1() {}
2、里面可以使用yield表达式,遇到yield会暂停执行,等待外面调用next返回对应状态;知道遇到return或者后面不再有执行语句
function* generator1() {
yield 1;
yield 2;
yield 3;
}
const g = generator1();
console.log(g.next())
console.log(g.next())
console.log(g.next())
console.log(g.next())
动态创建generator函数
const GeneratorFunction = Object.getPrototypeOf(function* () {}).constructor;
const generator1 = new GeneratorFunction(`
yield 1;
yield 2;
yield 3;
`)
const g = generator1();
console.log(g.next())
console.log(g.next())
判断是否是generator函数
const GeneratorFunction = Object.getPrototypeOf(function* () {}).constructor;
function* generator1() {
yield 1
}
function fn() {
console.log(1)
}
console.log(generator1.constructor === GeneratorFunction) // true
console.log(fn.constructor === GeneratorFunction) // false
符合可迭代协议和迭代器协议
function* generator() {
yield 1;
yield 2;
yield 3;
}
const g = generator();
for(let v of g){
console.log(v)
}
console.log(g.next());
传递参数
生成器函数本身可以接收参数;调用next函数的时候也可以额外传递参数
next函数的返回值是生成器传递出去的参数
function* generator(num1) {
let num2 = 0;
while (true) {
num2 = yield num1 + num2;
}
}
const g = generator(10);
console.log(g.next()); // 10
console.log(g.next(20)); // 30
console.log(g.next(30)); // 60
generator对象(迭代器对象)
generator函数执行返回的对象一般称为generator对象,JS中的对象模型也叫Generator
对象上的方法:
next() 获取下一个状态
return() 给定的值并结束生成器
function* generator1() {
yield 1;
yield 2;
yield 3;
}
const g = generator1();
console.log(g.next())
console.log(g.return(10))
console.log(g.next())
throw() 向生成器抛出异常,并恢复生成器的执行,返回带有done和value两个属性的对象;如果generator不捕获异常,会导致调用出错;捕获的时候也有返回值
function* generator() {
let val = 1;
while (true) {
try {
yield val++;
} catch (error) {
console.log('error:'+error)
}
}
}
const g = generator();
console.log(g.next());
console.log(g.throw(new Error('error')));
console.log(g.next());
不捕获会直接报错
function* generator() {
let val = 1;
while (true) {
yield val++;
}
}
const g = generator();
console.log(g.next());
console.log(g.throw(new Error('error')));
迭代器协议
next():
是一个函数,返回的对象中包含两个属性
done:如果迭代器可以产生序列中的下一个值为false,否则为true
value:迭代器返回的任意值
可迭代器协议:允许JS对象定义或者制度它们的迭代行为,比如用for of遍历
让普通对象成为可迭代对象:设置[Symbol.iterator]属性,值为一个无参数的函数,返回值是一个符合迭代器协议的对象
function makeRangeIterator(start = 0, end = Infinity, step = 1) {
let nextIndex = start;
let iterationCount = 0;
const rangeIterator = {
next: function () {
let result;
if (nextIndex < end) {
result = { value: nextIndex, done: false };
nextIndex += step;
iterationCount++;
return result;
}
return { value: iterationCount, done: true };
},
};
return rangeIterator;
}
let it = makeRangeIterator(1, 10, 2);
let result = it.next();
while (!result.done) {
console.log(result.value); // 1 3 5 7 9
result = it.next();
}
序列生成器
function* sequenceGenerator(start = 0,step = 1,end = Number.MAX_SAFE_INTEGER) {
let current = start;
while (current < end) {
yield current
current += step;
}
}
const g = sequenceGenerator(0,3,20);
console.log(g.next()); // 0
console.log(g.next()); // 3
console.log(g.next()); // 6
const g2 = sequenceGenerator(0,3,20);
console.log([...g2]) // [0, 3, 6, 9, 12, 15, 18]
状态机
function* stateMachineGenerator(states,state) {
const len = states.length;
let index = Math.max(states.findIndex(s => s === state), 0);
while (true) {
yield states[(++index) % len];
}
}
async函数
使用async关键字声明的函数,async函数的构造函数是AsyncFunction,允许在其中使用await关键,是调用Promise的一种简洁方式
如果await的不是一个Promise实例,那么会被包裹成一个Promise实例
async function test() {
const r1 = await 1;
const r2 = await 2;
console.log(r1 + r2);
}
test();
将test方法转换成es5
const _awaiter = function (thisArg,_arguments,p,generator) {
function adopt(value) {
return value instanceof Promise ? value : new Promise(function (resolve) { resolve(value)});
}
return new Promise(function(resolve,reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (error) {
reject(error)
}
}
function rejected(value) {
try {
step(generator.throw(value));
} catch (error) {
reject(error)
}
}
function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled,rejected)
}
generator = generator.apply(thisArg,_arguments || []);
step(generator.next())
})
}
function test() {
return _awaiter(this,void 0,void 0,function* () {
const r1 = yield 1;
const r2 = yield 2;
console.log(r1 + r2);
})
}
test()