Как остановить l oop, предоставив результат l oop? - PullRequest
0 голосов
/ 19 января 2020

У меня есть требование, например, мне нужно получить список родителей, пока не будет найден самый верхний родитель. Если мы достигнем самого верхнего родителя, мы получим его ref_by_id как ноль. так что мы можем остановить l oop. В этом сценарии я не получаю m в строке (1)

    var u = "select ref_by_id from users where id=$1";
    var m = hey.ref_by_id;
    while (m !== null) {

        query(u, [m], function (er2, parentsList) {
            //line(1)
            if (parentsList[0].ref_by_id != null) {
                customObj.push(parentsList[0].ref_by_id);
                m = parentsList[0].ref_by_id;
            }
        })
    }   

Вот как я могу передать идентификатор результата в m и снова l oop, пока он не будет нулевым.

1 Ответ

0 голосов
/ 20 января 2020

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

const myFakedata = {
  1: { ref_byid: null },
  2: { ref_byid: null },
  3: { ref_byid: 1 },
  4: { ref_byid: 3 },
  5: { ref_byid: 6 },
  6: { ref_byid: 4 }
};

// Fake query for test.
function query(sql, parameters, callback) {
  setTimeout(() => callback(null, [myFakedata[parameters[0]]]), 50);
}

function rersolveHierarchy(callback) {
  try {
    var path = [];
    var sql = "select ref_by_id from users where id=$1";

    function loop(currentId) {
      try {
        if (currentId === null) {
          //Loop finished
          return callback(null, path); // breaks the loop
        }
        currentId = +currentId;
        if (isNaN(currentId)) return callback(Error("currentId is not a number"));
        path.push(currentId);

        console.log("Search for", currentId);
        query(sql, [currentId], function(error, rows) {
          if (error) return callback(error);
          if (!rows || rows.length === 0) return callback(Error(`No rows for id ${currentId}`));
          if (rows.length > 1) return callback(Error(`Multiple rows for id ${currentId}`));
          loop(rows[0].ref_byid); // extract id for next loot iteration
        });
      } catch (error) {
        return callback(error);
      }
    }

    var hey = { ref_by_id: 5 }; // Test data
    loop(hey.ref_by_id); // Start loop
  } catch (error) {
    return callback(error);
  }
}

rersolveHierarchy(function(error, data) {
  if (error) return console.error(error);
  console.log(data.join(", "));
});

Это работает, но вы заметите, что вы не можете получить информацию из функции без обратного вызова. А как насчет ошибок? Если вы хотите, чтобы о них сообщали из функции, вам также нужно добавить обратный вызов для этого. И вам нужно объявить try / catch на ошибки перехвата, которые могут произойти, когда вы запускаете свой код, или ваш вызывающий никогда не будет уведомлен. Это быстро становится грязным.

Еще один способ решить эту проблему - Promise и asyn c с await . Обещание - это стандартизированный способ обработки асинхронных функций, позволяющий легко получить результат или ошибку позже в коде. Оператор await (добавлен в ES2017) ожидает разрешения для разрешения или выдает ошибку, если она была отклонена. Это облегчает использование асинхронных функций.

Вот пример:

const myFakedata = {
  1: { ref_byid: null },
  2: { ref_byid: null },
  3: { ref_byid: 1 },
  4: { ref_byid: 3 },
  5: { ref_byid: 6 },
  6: { ref_byid: 4 }
};

// Fake query for test.
function query(sql, parameters, callback) {
  setTimeout(() => callback(null, [myFakedata[parameters[0]]]), 50);
}


// Promise-wrapper for your query function.
// If there is an error the promise will be rejected
function pquery(sql, parameters) {
  return new Promise((resolve, reject) =>
    // calling your original query
    query(
      sql,
      parameters,
      // Callback function. if there is an error, reject the promise, otherwise resolve it
      (error, result) => (error ? reject(error) : resolve(result))
    )
  );
}

async function rersolveHierarchy(id) {
  var path, rows, currentId;
  const sql = "select ref_by_id from users where id=$1";
  path = []; // Start with empty array
  currentId = id;
  while (currentId !== null) {
    // make sure currentId is a number by using the unary plus operator
    currentId = +currentId;
    if (isNaN(currentId)) throw Error("currentId is not a number");

    // Check for infinity loop
    if (path.includes(currentId)) throw Error(`Loop detected: ${currentId}: ${path.join(",")}`);

    // What order do you want the items to be?
    // use "push" to add the latest to the end,
    // or "unshift" to add it at the begining
    path.push(currentId); //or path.unshift(currentId);

    console.log("Search for", currentId);
    rows = await pquery(sql, [currentId]); // Get next item

    // Check that only one row is returned.
    if (!rows || rows.length === 0) throw Error(`No rows for id ${currentId}`);
    if (rows.length > 1) throw Error(`Multiple rows for id ${currentId}`);
    currentId = rows[0].ref_byid; // extract id for next loot iteration
  }

  return path;
}

// Your function where you call resolveUserPath
async function myFunction() {
  const hey = { ref_by_id: 5 }; // Test data
  const path = await rersolveHierarchy(hey.ref_by_id);
  console.log("Path:", path.join(", "));
  return path;
}

// Always check for errors
myFunction().catch(error => console.error(error));
...