Ад обратного вызова означает, что вы находитесь внутри обратного вызова внутри другого обратного вызова, и он переходит к n-му вызову, пока ваши потребности не будут удовлетворены.
Давайте разберемся на примере поддельного вызова ajax с использованием API установки тайм-аута, допустим, у нас есть API рецептов, нам нужно загрузить все рецепты.
<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>
Чтобы загрузить данные конкретного рецепта, мы написали код внутри нашего первого обратного вызова и передали идентификатор рецепта.
Теперь предположим, что нам нужно загрузить все рецепты того же издателя рецепта с идентификатором 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, каждое обещание принимает обратный вызов, который вызывается, когда обещание полностью заполнено. Обратный вызов обещания имеет два варианта: разрешен или отклонен. Предположим, что ваш вызов API прошел успешно, вы можете вызвать метод решения и передать данные через разрешение , вы можете получить эти данные с помощью then () . Но если ваш API не работает, вы можете использовать reject, использовать catch, чтобы поймать ошибку. Помните, что обещание всегда используйте тогда для решения и ловите для отклонения
Давайте решим предыдущую адскую проблему обратного вызова с помощью обещания.
<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, который также будет возвращать обещание, и мы можем написать еще один метод then (), чтобы получить обещание разрешения для allRecipeOfAPublisher, я надеюсь, что на этом этапе вы сможете сделать это самостоятельно.
Итак, мы узнали, как создавать и использовать обещания, а теперь давайте упростим использование обещаний с помощью async / await, представленного в es8.
<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>
В приведенном выше примере мы использовали асинхронную функцию, потому что она будет работать в фоновом режиме, внутри асинхронной функции мы использовали ключевое слово await перед каждым методом, который возвращает или является обещанием, потому что ждать в этой позиции, пока это обещание не будет выполнено, другими словами в приведенные ниже коды до тех пор, пока getIds не завершится, или программа отклонения не перестанет выполнять коды, указанные ниже в этой строке, когда будут возвращены идентификаторы, затем мы снова вызвали функцию getRecipe () с идентификатором и подождали, используя ключевое слово await, пока не вернутся данные. Вот как мы наконец оправились от ада обратных вызовов.
async function getRecipesAw(){
const IDs = await getIds;
console.log(IDs);
const recipe = await getRecipe(IDs[2]);
console.log(recipe);
}
Чтобы использовать ожидание, нам понадобится асинхронная функция, мы можем вернуть обещание, поэтому используйте then для обещания разрешения и cath для отклонения обещания
из приведенного выше примера:
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);
});