什么是回调地狱
异步的JavaScript程序,或者说使用了回调函数的JavaScript程序,很难地去直观顺畅地阅读,大量的代码以下面这种方式结束。简单说,就是函数作为参数层层嵌套。代码以此种形式展现时无疑是不利于我们阅读与维护的。
setTimeout(function (name) {
var catList = name + ','
setTimeout(function (name) {
catList += name + ','
setTimeout(function (name) {
catList += name
console.log(catList)
}, 200, 'Lion')
}, 200, 'Jaguar')
}, 200, 'Panther')
// Panther,Janguar,Lion
解决回调地狱
- 拆解 function:将各步拆解为单个的 function
function buildCatList(list, returnVal, fn) {
setTimeout(function (name) {
var catList = list === '' ? name : list + ',' + name
fn(catList)
}, 200, returnVal)
}
buildCatList('', 'Panther', getJanguar)
function getJanguar(list) {
buildCatList(list, 'Janguar', getLynx)
}
function getLynx(list) {
buildCatList(list, 'Lion', print)
}
function print(list) {
console.log(list)
}
// Panther,Janguar,Lion
定时器中的回调函数处在外层函数中的作用域内,并通过参数传入,没有产生全局变量,没有重复代码。但是 function 拆分的方式其实仅仅只是拆分代码块,时常会不利于后续维护
- 通过 Promise 链式调用的方式
function buildCatList(list, returnVal) {
return new Promise(function (resolve, reject) {
setTimeout(function (name) {
var catList = list === '' ? name : list + ',' + name
resolve(catList)
}, 200, returnVal)
})
}
buildCatList('','Panther').then(function (res) {
return buildCatList(res, 'Janguar')
}).then(function (res) {
return buildCatList(res, 'Janguar')
})
// Panther,Janguar,Lion
Promise函数虽然改变了之前回调地狱的写法,但是在根本上还是函数套函数,看起来不是那么的美观
- 通过Generator函数暂停执行的效果方式
function getPanther(next) {
console.log('Panther')
next()
}
function getJaguar(next) {
console.log('Jaguar')
next()
}
function getLion(next) {
console.log('Lion')
next()
}
function* buildCatList() {
yield getPanther
yield getJaguar
yield getLion
}
//流程控制
function run(fn){
const gen = fn() // 调用 Generator 函数,返回一个遍历器对象 (Iterator)
function next() {
const result = gen.next() // 每次调用遍历器对象的 next() 方法,就会返回一个有着 value 和 done 两个属性的对象
if (result.done) return // 如果 result.done 的值等于 true 就结束
result.value(next)
// result.value 就是 yield 返回的值,是各个函数(方法)
// next作为入参,即本函数(方法)执行后,执行下一函数(方法)
}
next() // 这个方法被多次调用
}
run(buildCatList) // 开始执行
// Panther
// Jaguar
// Lion
通过generator虽然能提供较好的语法结构,但是毕竟generator与yield的语境用在这里多少还有些不太贴切
- 通过ES8的异步函数 async / await
async function render() {
await getPanther('Panther')
await getJaguar('Jaguar')
await getLion('Lion')
}
render()
function getPanther(name) {
console.log(name)
}
function getJaguar(name) {
console.log(name)
}
function getLion(name) {
console.log(name)
}
// Panther
// Jaguar
// Lion
代码简洁清晰,异步代码也具有了“同步”代码的结构