Автор оригинала: FreeCodeCamp Community Member.
Adham El Banhawy
Закрытые разработчики работают в вызовах все время во время создания приложений или тестирующего кода. Как разработчик, который довольно новичок, и знакомится с этими проблемами, я никогда не столкнулся с проблемой или неудобства или более часто или более запоминающимся – чем с Функции обратного вызова.
Я не собираюсь углубиться слишком глубоко в деталях обратного вызова и его плюсов и минусов или альтернатив, таких как обещания и Async/ждут. Для более яркого объяснения вы можете проверить Эта статья что объясняет их тщательно.
Обратный ад
Обратные вызовы – это полезная особенность JavaScript, которая позволяет сделать асинхронные вызовы. Они являются функциями, которые обычно передаются в качестве второго параметра на другую функцию, которая получает данные или выполнение операции ввода/вывода, которая требует времени для завершения.
Например, попробуйте сделать вызов API, используя Запрос Модуль или подключение к базе данных MongoDB. Но что, если оба звонков зависят друг от друга? Что если данные, которые вы получаете, это URL MongoDB, к которому вам нужно подключиться к?
Вам придется гнездить эти звонки внутри друг друга:
request.get(url, function(error, response, mongoUrl) {
if(error) throw new Error("Error while fetching fetching data");
MongoClient.connect(mongoUrl, function(error, client) {
if(error) throw new Error("MongoDB connection error");
console.log("Connected successfully to server");
const db = client.db("dbName");
// Do some application logic
client.close();
});
});Хорошо … Так где проблема? Ну, за одну вещь читаемость кода страдает от этой техники.
Сначала он может показаться в порядке, когда кодовая база маленькая. Но это не масштабируется хорошо, особенно если вы пойдете больше слоев глубже в вложенные обратные вызовы.
Вы получите множество закрывающих кронштейнов и фигурных скобок, которые путают вас и другие разработчики, независимо от того, насколько аккуратно отформатирован ваш код. Есть сайт под названием callbackhell Это касается этой конкретной проблемы.
Я слышу некоторые из вас, в том числе мои наилучшие себя, говоря мне обернуть его в async Функция тогда ждать Функция обратного вызова. Это просто не работает.
После того, что есть любой код кода после функции, которая использует обратные вызовы, этот код код будет выполнен и не будет Подожди для обратного вызова.
Вот эта ошибка, которую я сделал раньше:
var request = require('request');
// WRONG
async function(){
let joke;
let url = "https://api.chucknorris.io/jokes/random"
await request.get(url, function(error, response, data) {
if(error) throw new Error("Error while fetching fetching data");
let content = JSON.parse(data);
joke = content.value;
});
console.log(joke); // undefined
};
// Wrong
async function(){
let joke;
let url = "https://api.chucknorris.io/jokes/random"
request.get(url, await function(error, response, data) {
if(error) throw new Error("Error while fetching fetching data");
let content = JSON.parse(data);
joke = content.value;
});
console.log(joke); // undefined
};Некоторые более опытные Devs могут сказать: «Просто используйте другую библиотеку, которая использует обещания, чтобы сделать то же самое, например Axios , или просто использовать Fetch » Отказ Конечно, я могу в этом сценарии, но это просто убегает от проблемы.
Кроме того, это просто пример. Иногда вы можете быть заблокированы в использовании библиотеки, которая не поддерживает обещания без альтернатив. Как и использовать комплекты разработки программного обеспечения (SDK) для связи с платформами, такими как Amazon Web Services (AWS), Twitter или Facebook.
Иногда даже используя обратный вызов, чтобы сделать очень простой вызов с помощью быстрой операции ввода-вывода или CRUD, и другая логика зависит от его результатов. Но вы можете быть ограничены средой выполнения, как в Лямбда Функция Что убьет весь процесс после окончания основных потоков, независимо от любых асинхронных вызовов, которые не были завершены.
Решение 1 (легко): используйте модуль узла “UTIL”
Решение удивительно просто. Даже если вам немного неудобно с идеей обещаний в JavaScript, вы будете любить, как вы можете решить эту проблему, используя их.
Как отметил Erop и Robin в комментариях, Nodejs версии 8 и выше теперь поддерживают функции обратного вызова в обещаниях, используя встроенный Util модуль.
const request = require('request');
const util = require('util');
const url = "https://api.chucknorris.io/jokes/random";
// Use the util to promisify the request method
const getChuckNorrisFact = util.promisify(request);
// Use the new method to call the API in a modern then/catch pattern
getChuckNorrisFact(url).then(data => {
let content = JSON.parse(data.body);
console.log('Joke: ', content.value);
}).catch(err => console.log('error: ', err))Приведенный выше код решает проблему аккуратно используя Util.promisify Метод доступен из Nodejs Core Bibly.
Все, что вам нужно сделать, это использовать функцию обратного вызова в качестве аргумента для UTIL.PROMISIFY и хранить ее переменной. В моем случае это getchucknorrisfact Отказ Затем вы используете эту переменную в качестве функции, которую вы можете использовать как обещание с .then () и .catch () методы.
Решение 2 (вовлечено): превратить обратный вызов в обещание
Иногда, используя библиотеки запроса и UTIL просто невозможно, будь то из-за устаревшей среды/кодовой базы или выполнения запросов из браузера клиента, вы должны обещать обещание вокруг функции обратного вызова.
Давайте возьмем пример Чука Норриса выше и превратимся в обещание.
var request = require('request');
let url = "https://api.chucknorris.io/jokes/random";
// A function that returns a promise to resolve into the data //fetched from the API or an error
let getChuckNorrisFact = (url) => {
return new Promise(
(resolve, reject) => {
request.get(url, function(error, response, data){
if (error) reject(error);
let content = JSON.parse(data);
let fact = content.value;
resolve(fact);
})
}
);
};
getChuckNorrisFact(url).then(
fact => console.log(fact) // actually outputs a string
).catch(
error => console.(error)
);В указанном выше коде я положил обратный вызов Запрос Функция внутри обертки обещания Обещание (((разрешение, отклонение) => {//Функция обратного вызова}) Отказ Эта обертка позволяет нам позвонить в getchucknorrisfact функция как обещание с .then () и .catch () методы. Когда getchucknorrisfact называется, он выполняет запрос на API и ждет Для либо разрешать () или Отклонить () утверждение для выполнения. В функции обратного вызова вы просто передаете полученные данные в методы разрешения или отклонения.
После того, как данные (в этом случае удивительный чек Норрис факт) выясняется и передается в Resolver, getchucknorrisfact Выполняет тогда () метод. Это вернет результат, который вы можете Используйте внутри функции внутри тогда () Чтобы сделать желаемую логику – в этом случае отображать его к консоли.
Вы можете прочитать больше об этом в MDN веб-документы.