什么是回调地狱?
异步JavaScript或者使用回调的JavaScript很难直观的理解。 如:
fs.readdir(source, function (err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function (filename, fileIndex) {
console.log(filename)
gm(source + filename).size(function (err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
})
看到这个函数,感觉瞬间崩溃,金字塔形状的代码和最后}),感觉地狱来临。这被亲切的称为回调地狱。
产生回调地狱的原因?
回调地狱的原因是当编写人员以重上往下视觉方式编写JavaScript。很多人都会犯这个错误!在C,java,Ruby,Python等语言中,编写人员期望第一行发生的任何事情都会在第二行代码运行开始之前被完成,依次类推。就会产生上述结果。
什么是回调?
其实很多学习JavaScript的编写人员,都认为回调是JavaScript的特定事务。其实不然,回调只是JavaScript函数预定名称,在JavaScript中没有“回调”这一特殊事务。在使用异步编程中,异步函数不会立即返回结果,即“将要发生”,而不是现在。如i/O下载使用,在这种情况下,下载i/o可能需要很长的时间,但是你并不希望在等待下载时暂停其他进程(“阻塞页面”)。而是在函数中存储下载完成后应运行的代码,这是回调。
如何修复地狱回调?
回调地狱是由糟糕的编码实践引起的。幸运的是,编写更好的代码并不难! 您只需遵循三条规则:
保持代码浅
源代码:
var form = document.querySelector('form')
form.onsubmit = function formSubmit (submitEvent) {
var name = document.querySelector('input').value
request({
uri: "http://example.com/upload",
body: name,
method: "POST"
}, function postResponse (err, response, body) {
var statusMessage = document.querySelector('.status')
if (err) return statusMessage.value = err
statusMessage.value = body
})
}
优化代码:
document.querySelector('form').onsubmit = formSubmit
function formSubmit (submitEvent) {
var name = document.querySelector('input').value
request({
uri: "http://example.com/upload",
body: name,
method: "POST"
}, postResponse)
}
function postResponse (err, response, body) {
var statusMessage = document.querySelector('.status')
if (err) return statusMessage.value = err
statusMessage.value = body
}
模块化
创建模块文件xxx.js
module.exports.submit = formSubmit
function formSubmit (submitEvent) {
var name = document.querySelector('input').value
request({
uri: "http://example.com/upload",
body: name,
method: "POST"
}, postResponse)
}
function postResponse (err, response, body) {
var statusMessage = document.querySelector('.status')
if (err) return statusMessage.value = err
statusMessage.value = body
}
应用
var formUploader = require('xxx')
document.querySelector('form').onsubmit = formUploader.submit
处理每一个错误
前两个规则主要是关于使代码可读,但这个规则是关于使代码稳定。在处理回调时,您可以按照定义处理已分派的任务,在后台执行某些操作,然后成功完成或因故障而中止。任何有经验的开发人员都会告诉您,您永远不会知道这些错误何时发生,因此您必须始终计划这些错误。 使用回调,最常用的处理错误的方法是Node.js样式,其中回调的第一个参数始终为错误保留。
var fs = require('fs')
fs.readFile('/Does/not/exist', handleFile)
function handleFile (error, file) {
if (error) return console.error('Uhoh, there was an error', error)
// otherwise, continue on and use `file` in your code
}