本文以代码为主,不会对代码做详细的讲解,相信大家都可以看的懂哦。先从异步带来的回调地狱问题开始,然后总结出来了多种解决异步流程控制的代码写法,希望对大家有帮助。
回调地狱
function delayFunc(){
setTimeout(() => {
console.log("t1");
setTimeout(() => {
console.log("t2");
setTimeout(() => {
console.log("t3");
}, 1000);
}, 1000);
}, 1000);
}
delayFunc();
rxjs中的流的控制,subscribe里套subscribe,套一层的话,影响不是很大,如果套3层或3层以上,阅读起来很不友好,一般用switchMap来避免这种问题。
上面代码挺清楚的呀?一眼就看到执行顺序和逻辑?? NoNoNO
经典案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>回调地狱</title>
<style>
.box {
width: 100px;
height: 100px;
background: red;
position: absolute;
left: 0px;
top: 0px;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
function move(ele, dir, target, cb) {
let start = parseInt(getComputedStyle(ele, null)[dir]);
let speed = 5 * (target > start ? 1 : -1);
setTimeout(() => {
let temp = start + speed;
if (temp === target) {
cb && cb();
} else {
ele.style[dir] = temp + 'px';
move(ele, dir, target, cb);
}
}, 20);
}
let ele = document.querySelector('.box');
move(ele, 'left', 300, function() {
move(ele, 'top', 300, function() {
move(ele, 'left', 0, function() {
move(ele, 'top', 0, function() {
console.log('完成');
});
});
});
});
</script>
</body>
</html>
解决方案
具名函数
function delayFunc(msg, cb){
setTimeout(() => {
console.log(msg);
cb && cb(msg);
}, 1000);
}
function func1(){
delayFunc("t2", func2);
}
function func2(){
delayFunc("t3", func3);
}
function func3(){
delayFunc("t4");
}
delayFunc("t1", func1);
观察者模式
严格来说是,订阅发布模式:
let eventObj = new EventTarget();
eventObj.addEventListener("event1", function(e){
console.log(e);
});
eventObj.dispatchEvent(new CustomEvent("event1", {
detail: {
age: 20
}
}));
let eventObj = new EventTarget();
let num = 1;
function move(ele, dir, target) {
let start = parseInt(getComputedStyle(ele, null)[dir]);
let speed = 5 * (target > start ? 1 : -1);
setTimeout(() => {
let temp = start + speed;
if (temp === target) {
eventObj.dispatchEvent(new CustomEvent('myEvent' + num));
num++;
} else {
ele.style[dir] = temp + 'px';
move(ele, dir, target);
}
}, 20);
}
let ele = document.querySelector('.box');
move(ele, 'left', 300);
eventObj.addEventListener('myEvent1', function() {
move(ele, 'top', 300);
});
eventObj.addEventListener('myEvent2', function() {
move(ele, 'left', 0);
});
eventObj.addEventListener('myEvent3', function() {
move(ele, 'top', 0);
});
eventObj.addEventListener('myEvent4', function() {
console.log('运动完成');
});
thunk函数
thunk函数可用解决回调地狱的问题。thunk函数是只接收一个回调函数参数的函数,函数的thunk化指的是,讲一个多参函数变为一个只接收回调函数参数的函数。
先来一个简单的例子,函数中只有一个参数msg。
function delayFunc(msg, cb){
setTimeout(() => {
console.log(msg);
cb && cb();
}, 1000);
}
let delayFuncThunk = (msg) => (cb) => {
delayFunc(msg, cb)
};
let t1 = delayFuncThunk("t1");
let t2 = delayFuncThunk("t2");
let t3 = delayFuncThunk("t3");
let t4 = delayFuncThunk("t4");
// t1(() => t2(() => t3(() => t4())));
let arr = [t1, t2, t3, t4];
let gen = (arr) => {
return arr.reduceRight((prev, cur) => {
return () => cur(() => prev());
});
}
let g = gen(arr);
g();
用reduce函数怎么实现? 简单的来,可以把arr = [t4, t3, t2, t1];
思考?
如果函数中有不确定参数个数,如果优雅的实现thunk化?
function delayFunc(msg,title,name,arg1...){
...
}
let delayFuncThunk = ...
generator函数
generator函数设计的初衷,是为了优雅的实现迭代,并不是为了异步流程控制,但是可以使用generator函数来实现异步控制。
function delayFunc(msg, cb){
setTimeout(() => {
console.log(msg);
cb && cb();
}, 1000);
}
function * myGen(cb){
yield delayFunc("t1", cb);
yield delayFunc("t2", cb);
yield delayFunc("t3", cb);
yield delayFunc("t4", cb);
}
const runner = myGen(goNext);
function goNext(res){
runner.next(res);
}
runner.next();
优化下代码:
function run(gen){
let it = gen(goNext);
// function goNext(){
// it.next();
// }
function goNext(res){
it.next(res);
}
goNext();
}
run(myGen);
思考: 如果在generator函数中,yield有返回值,怎么写?
thunk + generator
function delayFunc(msg, cb){
setTimeout(() => {
console.log(msg);
cb && cb();
}, 1000);
}
let delayFuncThunk = (msg) => (cb) => {
delayFunc(msg, cb);
};
let t1 = delayFuncThunk("t1");
let t2 = delayFuncThunk("t2");
let t3 = delayFuncThunk("t3");
let t4 = delayFuncThunk("t4");
function * myGen(){
yield t1;
yield t2;
yield t3;
yield t4;
}
function run(gen){
const it = gen();
function goNext(){
const temp = it.next(...arguments);
if(!temp.done) {
temp.value(goNext);
}
}
goNext();
}
run(myGen);
第二种写法:
function delayFunc(msg, cb) {
setTimeout(() => {
console.log(msg);
cb && cb();
}, 1000);
}
let delayFuncThunk = (msg) => (cb) => {
delayFunc(msg, cb);
};
let t1 = delayFuncThunk("t1");
let t2 = delayFuncThunk("t2");
let t3 = delayFuncThunk("t3");
let t4 = delayFuncThunk("t4");
function* myGen() {
yield t1;
yield t2;
yield t3;
yield t4;
}
function run(gen){
const it = myGen();
// () => t1()
// () => t2(() => t1())
// () => t3(() => t2(() => t1()))
// () => t4(() => t3(() => t2(() => t1())))
function goNext(cb){
const temp = it.next();
if (!temp.done) {
if(cb) {
goNext(() => temp.value(cb));
}
else {
goNext(() => temp.value());
}
}
else {
cb();
}
}
goNext();
}
run(myGen);
promise
function delayFunc(msg, cb){
setTimeout(() => {
console.log(msg);
cb && cb();
}, 1000);
}
const delayFuncPromise = (msg) => {
return new Promise((resolve) => {
delayFunc(msg, resolve);
});
};
delayFuncPromise("p1")
.then(() => {
return delayFuncPromise("p2");
})
.then(() => {
return delayFuncPromise("p3");
})
.then(() => {
console.log("完成了!");
});
promise + generator
function delayFunc(msg, cb){
setTimeout(() => {
console.log(msg);
cb && cb();
}, 1000);
}
const delayFuncPromise = (msg) => {
return new Promise((resolve) => {
delayFunc(msg, resolve);
});
};
function *myGen(){
yield delayFuncPromise("p11");
yield delayFuncPromise("p22");
yield delayFuncPromise("p33");
}
function run(gen){
const it = gen();
function goNext(){
const temp = it.next(...arguments);
if(!temp.done) {
temp.value.then(goNext);
}
}
goNext();
}
run(myGen);
async/await
async/await语法糖天然支持Promise,是js中异步的终极解决方案。
function delayFunc(msg, cb){
setTimeout(() => {
console.log(msg);
cb && cb();
}, 1000);
}
const delayFuncPromise = (msg) => {
return new Promise((resolve) => {
delayFunc(msg, resolve);
});
};
async function myAsyncFunc(){
let res = await delayFuncPromise("ps1");
await delayFuncPromise("ps2");
await delayFuncPromise("ps3");
}
myAsyncFunc();
思考
柯里化
function myFunc1(arg1,arg2,arg3,arg4,arg5){
console.log(arg1,arg2,arg3,arg4,arg5);
return arg1 + arg2 + arg3 + arg4 + arg5;
}
function curryFunc(func){
let length = func.length;
return function curry(...args){
if (args.length < length) {
return function(){
return curry(...args, ...arguments);
};
}
return func(...args);
}
}
let f = curryFunc(myFunc1);
let res = f(1)(2)(3)(4)(5);
console.log(res);