Javascript-форум (https://javascript.ru/forum/)
-   Node.JS (https://javascript.ru/forum/node-js-io-js/)
-   -   Callback Hell (https://javascript.ru/forum/node-js-io-js/72317-callback-hell.html)

artichek 21.01.2018 15:46

Callback Hell
 
Приветствую. Нужно чтобы функция, если аккаунт существует, возвращала 1, если нет - 0. При первом вызове выдаёт undefined, дальше как нужно. Видимо что-то с обратными вызовами, помогите исправить:
const mysql = require('mysql')
let r
function isRegistered(id){
    var sel_query = con.query('SELECT * FROM `users` WHERE `userid`= ?', id, function GET(error, results, fields) {
        if (results.length != 0) {
            r = 1
        }
        else {
            r = 0
        }
        return r
    })
    return r
}

Aetae 22.01.2018 11:48

Колбэки работают примерно так:
function isRegistered(id, success, error){
    var sel_query = con.query('SELECT * FROM `users` WHERE `userid`= ?', id, function GET(error, results, fields) {
        if(error) return error(error, id);
        if(!results.length) return error(0, id);
        return success(results.length, id);
    })
}

function coninueIfRegistered(count, id){
  //дальнейший код если зарегестрирован
}

function coninueIfNotRegistered(reason, id){
  //дальнейший код если не зарегестрирован
}


isRegistered(id, coninueIfRegistered, coninueIfNotRegistered);

Можно пошаманить с промисами и async await, но пока с этим не разберётесь - лучше не надо.

Audaxviator 22.01.2018 12:05

isRegistered(bla-bla);

async function isRegistered(id) {
  var sel_query = await new Promise( resolve => {
    con.query('SELECT * FROM `users` WHERE `userid`= ?', id, function(error, results) {
      if(err) console.error(err);
      resolve(results);
    });
  }).then( result => {
    return result;
  };
  if(sel_query.length != 0) {
    console.log(1, sel_query);
  } else {
    console.log(0);
  }
}

Aetae 22.01.2018 12:10

Audaxviator, тогда уж так:

const mysql = require('mysql')

async function isRegistered(id){
  var r = await new Promise(resolve => con.query('SELECT * FROM `users` WHERE `userid`= ?', id, function(err, results) {
    if(err || !results.length) return resolve(false);
    return resolve(true);
  }));                                  
  return r
}


(async function(){
  //основной код тут  

  let r = await isRegistered(id);
  //...
}());

Только человеку не понимающему стути коллбэков это только вред принесёт.)

artichek 22.01.2018 12:18

@Aetae, @Audaxviator, сердечно благодарю за помощь, пойду учить матчасть :)

Валерия_05 19.02.2018 10:02

Aetae, скажите, а разве из js можно работать с БД? Через аякс + PHP понятно, можно.
А выше какие-то query запросы прямо в js идут)) Это как так? Разве так можно?
Для подключения к БД ведь логин и пароль нужен будет и его ведь не спрячешь в js. Это не безопасно.

Валерия_05 19.02.2018 10:04

А, всё я поняла. Это NodeJS там наверное всё иначе работает.
Извините.

Audaxviator 19.02.2018 12:50

Тэкс. Объясняем "на пальцах" суть колбеков в асинхронных методах.
Вот обычная советская функция, которая присваивает объявленной перед её вызовом переменной значение - так, "как все привыкли":
function foo() {
  a = 'aaa';
}

var a;
foo();
console.log(a);    // 'aaa'

Полный успех.
А давайте сделаем присвоение значения переменной а - асинхронным, вот так:
function foo() {
  process.nextTick( () => {
    a = 'aaa';
  });
}

var a;
foo();
console.log(a);    // undefined

Ошибки не случилось - то есть функция переменную видела, к ней благополучно обращалась, - но в текущем "тике" переменной значение, естественно, не присвоено. Причём, оно было ваабщета присвоено - но нам оказалось уже недоступно.
А давайте посмотрим, есть ли оно в следующем тике?
function foo() {
  process.nextTick( () => {
    a = 'aaa';
  });
}
 
var a;
foo();
console.log(a);  // undefined

process.nextTick ( () => {
  console.log(a);  // 'aaa'
});

Таки есть!
И чтобы нам её (переменную с присвоенным значением) всё таки добыть и употребить, как раз и потребуется колбек-функция. Тырц:
function foo(cb) {
  setImmediate( () => {  // setImmediate - это правильная директива для асинхронного исполнения
    a = 'aaa';
    cb(a);
  });
}

var a;
foo( data => {
  console.log(data);  // 'aaa'
});
console.log(a);  // а тут, понятно, как и прошлый раз - undefined

Причём, сначала-то будет выведено undefined из последней строчки, а потом - 'aaa' из колбека.
Вот и вся несложная мудрость. Из которой, надеюсь, должно быть понятно, что любой асинхронный метод любую переменную "снаружи" взять-то может - а вот воспользоваться тем, что он с ней сделает, можно только "внутри": либо внутри собственно колбека этого метода, либо внутри промиса (в который обернут, опять же, вызов этого метода с вызовом его колбека) - но "внутри", и не иначе.
В "борьбе с асинхронностью" приходится всё время придумывать конструкции для добывания нужных значений в нужном месте. И это хорошо.
Извените за внимание.

Alexandroppolus 19.02.2018 13:40

Audaxviator,
не совсем понял суть замены process.nextTick на setImmediate (и комментария к последней)

Audaxviator 19.02.2018 14:28

process.nextTick было типа в демонстрационных целях - буквально "в следующем тике". Нопремер, setTimeout - это тот же nextTick, но с минимальной задержкой 4 миллисекунды (если написать 0 - всё равно будет 4). Но во-первых, задержка при асинхронном исполнении не нужна, а во-вторых, nextTick даёт приоритет первому некст-тику в коде. А если несколько асинхронных операций?
Короче, это не замена, а использование правильной для асинхронного исполнения функции во втором случае.
Ну, или чтобы всех запутать или заинтриговать.

(Словом, в этом "примитивном" примере будет одинаково работать и то, и то. А "в реальной жизни" используется setImmediate. Оно когда-то было предложено, вроде бы, Микрософтом, и в ихнем Эксплорере, в интерпретаторе JavaScript, присутствует (в других браузерах - не знаю, не проверял). И в Node - конечно, было сразу нативно вписано.)

Alexandroppolus 19.02.2018 17:45

Audaxviator,
но по сути обе функции выполнят свой колбэк после всех синхронных действий текущего тика.
Просто process.nextTick оформляет свою задачу как "микротаск" - добавляет в специальную очередь текущего исполняемого "таска" (витка основного событийного цикла), все пункты из очереди выполняются сразу за самим таском, только потом стартует новый таск.
А setImmediate сразу ставит отдельный таск.

Потому, в принципе неважно что там - nextTick, setImmediate, прочее... - все равно присвоение (из примера) случится после тех действий, которые идут за вызовом foo()

Audaxviator 19.02.2018 18:11

Не, ну я пытался показать сам принцип, потому что задрала уже тема "ужаса-ужаса асинхронности", и вопросы типа "а почему оно undefined? а куда делось значение?".
Чего тут такого сверхъестественного?
А модули для Ноды я всё равно писать не буду, поэтому в совсем-совсем тонкости я и не вникаю, в исходные коды не заглядывал. Асинхронность - это весело и изящно.

Aetae 20.02.2018 00:30

Цитата:

Сообщение от Валерия_05 (Сообщение 478518)
А, всё я поняла. Это NodeJS там наверное всё иначе работает.
Извините.

Ставлю рубь против копейки: всё ещё будет. Появится javascript аналог jdbc, разумеется "супер секурный", и будут все тягать базу прям с клиента. И под это дело конечно ещё вагончик кривых технологий подвезут, с прицепом из дырок и атак на отказ в обслуживании.)

Alexandroppolus 20.02.2018 01:19

Цитата:

Сообщение от Aetae
Ставлю рубь против копейки: всё ещё будет. Появится javascript аналог jdbc, разумеется "супер секурный", и будут все тягать базу прям с клиента.

Такое даже иногда бывает, если на сайте нет защиты от sql инъекций (да, бывает и в наши дни). Пишется "пользовательский jdbc" :)

nerv_ 25.02.2018 12:36

Цитата:

Сообщение от Audaxviator (Сообщение 475926)
isRegistered(bla-bla);

async function isRegistered(id) {
  var sel_query = await new Promise( resolve => {
    con.query('SELECT * FROM `users` WHERE `userid`= ?', id, function(error, results) {
      if(err) console.error(err);
      resolve(results);
    });
  }).then( result => {
    return result;
  };
  if(sel_query.length != 0) {
    console.log(1, sel_query);
  } else {
    console.log(0);
  }
}

как бы это вам сказать... Все придумано до нас в данном случае -- https://nodejs.org/dist/latest-v8.x/...isify_original

const {promisify} = reuqire('util')

const query = promisify(con.query)

isRegistered(bla-bla);

async function isRegistered(id) {
  const result = await query('SELECT * FROM `users` WHERE `userid`= ?', id)
  console.log('result', result)
}


Но проще бывает версию драйвера с промисами подключить...
Или еще проще, современные драйвера позволяют использовать оба подхода (коллбеки и промисы). Соответственно, если не передавать коллбек, вернется промис.

Audaxviator 25.02.2018 13:02

Цитата:

Сообщение от nerv_ (Сообщение 479095)
Все придумано до нас в данном случае

Для кого придумано?
Либо для того, кто реально на этом работает - пишет сайты-коды и вот это всё, либо для того - кто сам придумывать не любит или не может.
Ни то, ни другое ко мне не относится.


Часовой пояс GMT +3, время: 09:13.