回调地狱意味着您在另一个回调内部的回调中,并且它会进行第 n 次调用,直到您的需求未被满足。
让我们通过一个使用 set timeout API 的虚假 ajax 调用示例来理解,假设我们有一个 recipe API,我们需要下载所有 recipe。
<body>
<script>
function getRecipe(){
setTimeout(()=>{
const recipeId = [83938, 73838, 7638];
console.log(recipeId);
}, 1500);
}
getRecipe();
</script>
</body>
在上面的示例中,当计时器到期 1.5 秒后,内部回调代码将执行,换句话说,通过我们的虚假 ajax 调用,所有配方将从服务器下载。现在我们需要下载特定的配方数据。
<body>
<script>
function getRecipe(){
setTimeout(()=>{
const recipeId = [83938, 73838, 7638];
console.log(recipeId);
setTimeout(id=>{
const recipe = {title:'Fresh Apple Juice', publisher:'Suru'};
console.log(`${id}: ${recipe.title}`);
}, 1500, recipeId[2])
}, 1500);
}
getRecipe();
</script>
</body>
为了下载特定的配方数据,我们在第一个回调中编写了代码并传递了配方 ID。
现在假设我们需要下载 ID 为 7638 的食谱的同一发布者的所有食谱。
<body>
<script>
function getRecipe(){
setTimeout(()=>{
const recipeId = [83938, 73838, 7638];
console.log(recipeId);
setTimeout(id=>{
const recipe = {title:'Fresh Apple Juice', publisher:'Suru'};
console.log(`${id}: ${recipe.title}`);
setTimeout(publisher=>{
const recipe2 = {title:'Fresh Apple Pie', publisher:'Suru'};
console.log(recipe2);
}, 1500, recipe.publisher);
}, 1500, recipeId[2])
}, 1500);
}
getRecipe();
</script>
</body>
为了满足我们的需求,即下载出版商名称 suru 的所有配方,我们在第二个回调中编写了代码。很明显,我们编写了一个回调链,称为回调地狱。
如果你想避免回调地狱,你可以使用 Promise,它是 js es6 的特性,每个 Promise 都有一个回调,当 Promise 满时调用。承诺回调有两个选项,要么被解决,要么被拒绝。假设您的 API 调用成功,您可以调用 resolve 并通过 resolve 传递数据,您可以使用then()获取此数据。但是如果您的 API 失败,您可以使用拒绝,使用catch来捕获错误。记住一个 promise 总是用then表示 resolve 和catch表示拒绝
让我们使用 Promise 解决之前的回调地狱问题。
<body>
<script>
const getIds = new Promise((resolve, reject)=>{
setTimeout(()=>{
const downloadSuccessfull = true;
const recipeId = [83938, 73838, 7638];
if(downloadSuccessfull){
resolve(recipeId);
}else{
reject('download failed 404');
}
}, 1500);
});
getIds.then(IDs=>{
console.log(IDs);
}).catch(error=>{
console.log(error);
});
</script>
</body>
现在下载特定的食谱:
<body>
<script>
const getIds = new Promise((resolve, reject)=>{
setTimeout(()=>{
const downloadSuccessfull = true;
const recipeId = [83938, 73838, 7638];
if(downloadSuccessfull){
resolve(recipeId);
}else{
reject('download failed 404');
}
}, 1500);
});
const getRecipe = recID => {
return new Promise((resolve, reject)=>{
setTimeout(id => {
const downloadSuccessfull = true;
if (downloadSuccessfull){
const recipe = {title:'Fresh Apple Juice', publisher:'Suru'};
resolve(`${id}: ${recipe.title}`);
}else{
reject(`${id}: recipe download failed 404`);
}
}, 1500, recID)
})
}
getIds.then(IDs=>{
console.log(IDs);
return getRecipe(IDs[2]);
}).
then(recipe =>{
console.log(recipe);
})
.catch(error=>{
console.log(error);
});
</script>
</body>
现在我们可以编写另一个调用allRecipeOfAPublisher的方法,例如 getRecipe,它也将返回一个 Promise,我们可以编写另一个 then() 来接收 allRecipeOfAPublisher 的 resolve Promise,我希望此时您可以自己完成。
所以我们学习了如何构造和使用 Promise,现在让我们使用 es8 中引入的 async/await 来更轻松地使用 Promise。
<body>
<script>
const getIds = new Promise((resolve, reject)=>{
setTimeout(()=>{
const downloadSuccessfull = true;
const recipeId = [83938, 73838, 7638];
if(downloadSuccessfull){
resolve(recipeId);
}else{
reject('download failed 404');
}
}, 1500);
});
const getRecipe = recID => {
return new Promise((resolve, reject)=>{
setTimeout(id => {
const downloadSuccessfull = true;
if (downloadSuccessfull){
const recipe = {title:'Fresh Apple Juice', publisher:'Suru'};
resolve(`${id}: ${recipe.title}`);
}else{
reject(`${id}: recipe download failed 404`);
}
}, 1500, recID)
})
}
async function getRecipesAw(){
const IDs = await getIds;
console.log(IDs);
const recipe = await getRecipe(IDs[2]);
console.log(recipe);
}
getRecipesAw();
</script>
</body>
在上面的例子中,我们使用了一个 async 函数,因为它会在后台运行,在 async 函数中,我们在每个返回或者是一个 promise 的方法之前使用了await关键字,因为在那个位置上等待直到这个 promise 完成,换句话说,在波纹管代码直到 getIds 完成解决或拒绝程序将在 ID 返回时停止执行该行下面的代码,然后我们再次使用 id 调用 getRecipe() 函数并使用 await 关键字等待直到数据返回。所以这就是我们最终从回调地狱中恢复过来的方式。
async function getRecipesAw(){
const IDs = await getIds;
console.log(IDs);
const recipe = await getRecipe(IDs[2]);
console.log(recipe);
}
要使用await,我们需要一个异步函数,我们可以返回一个promise,所以使用then来解决promise和cath来拒绝promise
从上面的例子:
async function getRecipesAw(){
const IDs = await getIds;
const recipe = await getRecipe(IDs[2]);
return recipe;
}
getRecipesAw().then(result=>{
console.log(result);
}).catch(error=>{
console.log(error);
});