callback回调函数
- 优点:简单,容易理解。
- 缺点:代码可读性差,不易维护,耦合度高,层层嵌套造成回调地狱。
const funcOne = (num, callback) => {
const newNum = num + 5;
// 异步操作,使用setTimeout模拟
setTimeout(() => callback(newNum), 1000)
}
const funcTwo = (num, callback) => {
const newNum = num + 10;
// 异步操作,使用setTimeout模拟
setTimeout(() => callback(newNum), 2000)
}
const funcThree = (num, callback) => {
const newNum = num + 20;
// 异步操作,使用setTimeout模拟
setTimeout(() => callback(newNum), 3000)
}
const start = () => {
const num = 5;
console.log(num);
funcOne(num, function(funcOneReturnvalue) {
console.log(funcOneReturnvalue);
funcTwo(funcOneReturnvalue, function(funcTwoReturnvalue) {
console.log(funcTwoReturnvalue);
funcThree(funcTwoReturnvalue, function(funcThreeReturnvalue) {
console.log(funcThreeReturnvalue);
});
});
});
}
start();
事件监听(发布订阅模式)
- 优点:更符合模块化思想,编写自己的监听器的时候可以做很多优化,从而更好的监听程序的运行。
- 缺点:整个程序变成了事件驱动,或多或少影响了流程,而且每次使用都要注册事件监听器然后触发,比较麻烦。
document.body.addEventListener('click', function () {
console.log('click');
});
// 实现发布订阅
type EventHandler = (data?: any) => void;
class EventEmitter {
handlersMapping: {
[key: string]: Array<EventHandler>;
} = {};
// 注册事件和处理函数
on(type: string, handler: EventHandler) {
let handlers = this.handlersMapping[type];
if (!handlers) {
handlers = this.handlersMapping[type] = [];
}
handlers.push(handler);
}
// 销毁事件和处理函数
off(type: string, handler: EventHandler) {
let handlers = this.handlersMapping[type] || [];
if (!handler) {
// 没有传入要销毁的方法时,清空时间对应所有方法
this.handlersMapping[type] = [];
} else {
const targetIndex = handlers.findIndex((handlerItem: EventHandler) => handlerItem === handler);
if (targetIndex !== -1) {
handlers.splice(targetIndex, 1);
}
}
}
// 触发某事件所有回调并带参数
emit(type: string, payload?: any) {
let handlers = this.handlersMapping[type] || [];
handlers.forEach(handler => handler(payload));
}
}
export default new EventEmitter();
// 内部组件进行监听某个事件执行在内部组件中注册处理函数,在全局组件中满足条件时触发内部组件所监听的方法
const dyxtest = () => {}
useEffect(() => {
eventBus.on("dyxtest", dyxtest);
return () => {
eventBus.off("dyxtest", dyxtest);
};
}, []);
// 全局组件触发
eventBus.emit("dyxtest");
Promise
- 优点:避免了回调函数层层嵌套,可读性更强。链式操作,可以在then中继续写Promise对象并return,然后继续调用then进行回调操作。
- 缺点:Promise对象一旦创建就会立即执行,不能中途取消。
const funcOne = num => {
return new Promise((resolve, reject) => {
const newNum = num + 5;
setTimeout(() => resolve(newNum), 1000)
})
}
const funcTwo = num => {
return new Promise((resolve, reject) => {
const newNum = num + 10;
setTimeout(() => resolve(newNum), 2000)
})
}
const funcThree = num => {
return new Promise((resolve, reject) => {
const newNum = num + 20;
setTimeout(() => resolve(newNum), 3000)
})
}
const start = () => {
const num = 5;
console.log(num);
funcOne(num).then(resOne => {
console.log(resOne);
return funcTwo(resOne);
}).then(resTwo => {
console.log(resTwo);
return funcThree(resTwo);
}).then(resThree => {
console.log(resThree);
});
}
start();
Generator函数
- 优点:优雅的流程控制方法,允许函数被中断地执行。
- 缺点:Generator函数的执行必须依赖执行器,对于只做异步处理还是不太方便。
const funcOne = () => {
return new Promise(resolve => {
setTimeout(() => console.log(1), 1000);
})
}
const funcTwo = () => {
return new Promise(resolve => {
setTimeout(() => console.log(2), 2000);
})
}
const funcThree = () => {
return new Promise(resolve => {
setTimeout(() => console.log(3), 3000);
})
}
function* start() {
console.log('start');
yield funcOne();
yield funcTwo();
yield funcThree();
return 'dyx';
}
const startFun = start(); // 仅生成指针对象,并不执行
startFun.next() // start 1
startFun.next() // 2
startFun.next() // 3
async函数
- 优点:内置执行器,语义更好,适用性更广。
- 缺点:误用await可能会导致性能问题,因为await会阻塞代码。
const funcOne = num => {
return new Promise((resolve, reject) => {
const newNum = num + 5;
setTimeout(() => resolve(newNum), 1000)
})
}
const funcTwo = num => {
return new Promise((resolve, reject) => {
const newNum = num + 10;
setTimeout(() => resolve(newNum), 2000)
})
}
const funcThree = num => {
return new Promise((resolve, reject) => {
const newNum = num + 20;
setTimeout(() => resolve(newNum), 3000)
})
}
const start = async () => {
const num = 5;
console.log(num);
const firstAddNum = await funcOne(num);
console.log(firstAddNum);
const secondAddNum = await funcTwo(firstAddNum);
console.log(secondAddNum);
const thirdAddNum = await funcThree(secondAddNum);
console.log(thirdAddNum);
}
start();
async/await对比promise的优缺点
- 优点
- 真正的串行的同步写法,代码阅读相对容易。
- 对于条件语句和其他流程语句比较友好,可以直接写到判断条件里面。
- 缺点
- 无法处理promise返回的reject对象,要借助try catch。
async/await与Generator的差异
- async/await自带执行器,不需要手动调用next()就能自动执行下一步。
- async函数返回值是Promise对象,而Generator返回的是生成器对象。
- await能够返回Promise的resolve/reject的值。
利用Generator实现async函数
async/await实际上是对Generator(生成器)的封装,是一个语法糖。通过step函数实现Generator函数的自执行,当Generator函数的.next()执行返回done: true时表示执行完毕.
function asyncToGenerator(generatorFunc) {
return function() {
const gen = generatorFunc.apply(this, arguments);
return new Promise((resolve, reject) => {
function step(key, arg) {
let generatorResult;
try {
generatorResult = gen[key](arg);
} catch (error) {
return reject(error);
}
const { value, done } = generatorResult;
if (done) {
return resolve(value);
} else {
return Promise.resolve(value).then(val => step('next', val), err => step('throw', err));
}
}
step("next");
})
}
}