回调地狱意味着您处于另一个回调内部的回调中,并且它会进行第 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 被填满时会调用它。Promise回调有两个选项,要么解决,要么拒绝。假设你的API调用成功,您可以打电话的决心,并通过传递数据解析,您可以通过使用得到这个数据则() 。但是如果你的 API 失败了,你可以使用拒绝,使用catch来捕捉错误。记住一个Promise总是使用then来解决和捕获拒绝
让我们使用 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 的解决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);
});