Что такое callback hell и почему это происходит?

ад обратного звонка

Ад обратного вызова — распространенная проблема при написании кода на асинхронном JavaScript. В асинхронной функции мы используем обратные вызовы для запроса и извлечения значения из других функций. Операция будет более сложной, если нам потребуется больше обратных вызовов на линии. Например, вы вызываете ресурс и много раз ждете результата. Этот вид операции приведет к тому, что функция обратного вызова будет иметь многовложенную структуру. Это будет похоже на пирамиду, поэтому это явление также известно как один из видов «пирамиды судьбы».

asyncJs(url, success, failure){
	if(failure){
		console.log(eer)
	}
	else{
		asyncJs(url2, success, failure){
			if(failure){
				console.log(eer)
			}
			else{
				asyncJs(url2, success, failure){
					asyncJs(url, success, failure){
						...
					}
				}
			}
		}
	}
}

JavaScript является однопоточным

Помимо сложных обратных вызовов, причина, по которой код JavaScript легко превращается в «ад обратных вызовов», заключается в его однопоточном механизме.

Проведем эксперимент:

console.log('First line')
setTimeout(()=>console.log('Second'),0)
console.log('Third line')

Порядок будет:

  1. "Первая строка"
  2. «Третья линия»
  3. "Вторая линия"

Причина этого явления в том, что такие методы, как setTimeout, обрабатываются браузером в фоновом режиме. Пока третья строка выполнена, браузер возвращает результат.

Вы можете видеть, что JavaScript является однопоточным. Таким образом, для выполнения асинхронных операций, таких как ожидание изображения, запрос данных из общедоступного API или просто ожидание выполнения другого вычисления, что-то может пойти не так. Например, страница завершает загрузку до отображения данных во внешнем интерфейсе. В прошлом разработчик использовал обратный вызов для решения этой проблемы, но создавал еще одну, ад обратного вызова.

Чтобы избежать ада обратного вызова

Здесь я настроил простой обратный вызов.

let myRequest = (url,resolve, reject) => {
	let delay = Math.floor((Math.random() * 3500))+ 500;
  setTimeout( function() {
	  if(delay > 3000){
		  resolve( `Success! This is the response from" ${url}`)
	  }
	  else{
	  	  reject('Timeout')
	  }
  }, delay)
})

Давайте сделаем трехуровневый обратный вызов.

myRequest('exampleurl.com/q?res01',
(response)=>{
    console.log('it worked!(1)');
    console.log(response);
    
    myRequest('exampleurl.com/q?res02',
    (response)=>{
        console.log('it worked!(2)');
        console.log(response);
        
        myRequest('exampleurl.com/q?res03',
        (response)=>{
            console.log('it worked!(3)');
            console.log(response);
            
        },(err)=>{
            console.log('ERROR(1)',err);
        })
    },(err)=>{
        console.log('ERROR(2)',err);
    })
},(err)=>{
    console.log('ERROR(3)',err);
})

Чтобы исправить этот беспорядок, мы можем использовать Promise, чтобы сделать коды чище.

Однако на первый взгляд он все еще выглядит как пирамида.

fakeRequest с использованием обещания (неприятно)

const fakeRequestPromise = (url) => {
    return new Promise((resolve, reject) => {
        let delay = Math.floor((Math.random() * 3500))+ 500;
        setTimeout(() => {
            if (delay >3000){
                    reject('Timeout');
            }
            else{
                resolve(`Success! This is the response from" ${url}`);
            }
        },delay);
    });
}
request = fakeRequestPromise('exampleurl.com/q?res01')
.then(()=>{
    console.log('It worked!');
    fakeRequestPromise('exampleurl.com/q?res02')
    .then(()=>{
        console.log('It worked!(2)');
        fakeRequestPromise('exampleurl.com/q?res03')
        .then(()=>{
            console.log('It worked!(3)');
        })
        .catch(()=>{
            console.log('ERROR!(3)');
        })
    })
    .catch(()=>{
        console.log('ERROR!(2)');
    })
})
.catch(()=>{
    console.log('ERROR!');
})

Одной из особенностей promise является то, что нам не нужно перехватывать ошибку для каждого then(). Пока мы связываем действия then вместе, Promise будет ловить ошибку для каждого из них.

fakeRequest с использованием Promise (улучшено)

request = fakeRequestPromise('exampleurl.com/q?res01')
.then(()=>{
    console.log('It worked!');
    fakeRequestPromise('exampleurl.com/q?res02')
	  })
.then(()=>{
	  console.log('It worked!(2)');
    fakeRequestPromise('exampleurl.com/q?res03')
		})
.then(()=>{
	  console.log('It worked!(3)');
		})
.catch(()=>{
    console.log('ERROR!');
})

Заключить

  • Js является однопоточным.
  • Промисы могут упростить процесс асинхронного программирования, чтобы избежать ада обратного кода.