回调地狱或尝试捕捉地狱(恐怖之塔)
什么是“回调函数”?
回调函数通常用作另一个函数的参数。
接收回调函数的函数通常会从数据库中获取数据、发出 API 请求、下载文件,这通常需要一段时间。
假设从 API 获取一些数据,请求大约需要 2 秒钟才能完成。
现在,您可以选择等待 API 调用完成,然后再显示您的 UI。
或者,您可以显示其他所有内容,并在需要显示 API 数据的地方显示加载器。
在 API 函数中,我们传递某种“回调函数”,该函数会将加载器替换为实际数据,以便在收到 API 的响应后进行处理。
它使用数据调用回调函数,然后我们的回调函数替换加载器。
让我们看看实际效果:
function getDataFromAPI(callbackFunction) {
fetchSomeData().then((data) => {
callbackFunction(data);
});
}
getDataFromAPI(function replaceLoaderWithData(data) {
// your awesome logic to replace loader with data
});
或者
// from w3schools
function myDisplayer(sum) {
document.getElementById('demo').innerHTML = sum;
}
function myCalculator(num1, num2, myCallback) {
let sum = num1 + num2;
myCallback(sum);
}
myCalculator(5, 5, myDisplayer);
好的,你已经知道了。我们今天不是要学习回调函数是什么。
什么是“回调地狱”?
如果你的应用程序逻辑不太复杂,少量回调函数似乎无伤大雅。
但随着项目需求的增加,你会很快发现自己堆积了多层嵌套的回调函数。
像这样:
getAreas(function (areas) {
getTowns(function (towns) {
getCities(function (cities) {
getCountries(function (countries) {
getContinents(function (continents) {
getPlanets(function (planets) {
getSolarSystems(function (solarSystems) {
getGalaxies(function (galaxies) {
// Welcome to the callback hell...
});
});
});
});
});
});
});
});
当然,我们可以使用 JavaScriptPromise并迁移到.then& .catch。
getAreas().then(function (areas) {
getTowns().then(function (towns) {
getCities().then(function (cities) {
getCountries().then(function (countries) {
getContinents().then(function (continents) {
getPlanets().then(function (planets) {
getSolarSystems().then(function (solarSystems) {
getGalaxies().then(function (galaxies) {
// Welcome to the callback hell AGAIN...
});
});
});
});
});
});
});
});
恭喜!欢迎来到回调地狱。
回调地狱,又称厄运金字塔,是一个俚语,用来描述数量庞大的嵌套“if”语句或函数。
异步等待救援!
Async/await 就像天堂一样,因为它避免了回调地狱或厄运金字塔,而是以清晰的逐行格式编写异步代码。
上述代码修改为:
// assuming the environment supports direct async function
const areas = await getAreas();
const towns = await getTowns();
const cities = await getCities();
const countries = await getCountries();
const continents = await getContinents();
const planets = await getPlanets();
const solarSystems = await getSolarSystems();
const galaxies = await getGalaxies();
😳😲😳
// now this... looks awesome!!!
但...
这很棒,直到出现错误处理问题,因为最终你会得到一个恐怖的 try-catch 代码塔!
你所有优美的单行代码都会神奇地扩展成至少五行代码……
// assuming the environment supports direct async function
try {
const areas = await getAreas();
} catch (err) {
// handleError(err)
}
try {
const towns = await getTowns();
} catch (err) {
// handleError(err)
}
try {
const cities = await getCities();
} catch (err) {
// handleError(err)
}
try {
const countries = await getCountries();
} catch (err) {
// handleError(err)
}
// ... and so on.
你可以找到一种简单的方法,那就是在每个 Promise 的末尾添加 catch 方法。
// assuming the environment supports direct async function
const areas = await getAreas().catch((err) => handleError(err));
const towns = await getTowns().catch((err) => handleError(err));
const cities = await getCities().catch((err) => handleError(err));
const countries = await getCountries().catch((err) => handleError(err));
const continents = await getContinents().catch((err) => handleError(err));
const planets = await getPlanets().catch((err) => handleError(err));
const solarSystems = await getSolarSystems().catch((err) => handleError(err));
const galaxies = await getGalaxies().catch((err) => handleError(err));
这样看起来好多了,但是!还是有点重复。
另一个更好的选择是创建标准化的错误处理函数。
该函数会先解析 Promise,然后返回一个数组。
数组中的第一个元素是数据,第二个元素是错误信息。
如果出现错误,则数据为空,并且错误信息定义如下:
async function promiseResolver(promise) {
try {
const data = await promise();
return [data, null];
} catch (err) {
return [null, err];
}
}
现在,当你在代码中调用此函数时,你可以对其进行解构,从而得到一个简洁的单行代码并进行错误处理;
或者,如果你想对错误执行其他操作,可以使用常规的 if 语句。
你的主函数大概会是这样:
// assuming the environment supports direct async function
const [areas, areasErr] = await promiseResolver(getAreas);
const [towns, townsErr] = await promiseResolver(getTowns);
const [cities, citiesErr] = await promiseResolver(getCities);
if (citiesErr) {
// do something
}
const [countries, countriesErr] = await promiseResolver(getCountries);
const [continents, continentsErr] = await promiseResolver(getContinents);
const [planets, planetsErr] = await promiseResolver(getPlanets);
const [solarSystems, solarSystemsErr] = await promiseResolver(getSolarSystems);
const [galaxies, galaxiesErr] = await promiseResolver(getGalaxies);
if (galaxiesErr) {
// do something
}
// ... and so on.
就这些啦!希望对你们有帮助,下次再见😉
文章来源:https://dev.to/ovi/callback-hell-or-try-catch-hell-tower-of-terror-5h78