Возвращаемое значение из onreadystatechange с обратным вызовом

Я пытаюсь вернуть значение из вызова onreadystatechange AJAX... Я нашел эту страницу: ссылка на stackru. Я хоть и работал, но понял, что не имеет значения, добавлять или удалять функцию fn. Следующий код работает:

username_is_available();

function username_is_available() {
  var username = document.getElementById('username').value;

  get_data('username', username, function(returned_value) {
   if (returned_value == 'true') {
     document.getElementById('username_err').innerHTML = 'Taken';
    } else {
     document.getElementById('username_err').innerHTML = 'Available';
    };
  });
}

function get_data(data_type, data, fn) {
  var xmlhttp = new XMLHttpRequest();
  xmlhttp.onreadystatechange = function() {
  if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
    fn(xmlhttp.responseText);
  }
};
  xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
  xmlhttp.send();
}

Все работает нормально, но это не моя цель, я хотел бы функцию username_is_available(), которая возвращает true, если имя пользователя действительно доступно. Вместо этого здесь происходит действие (изменен innerHTML). И если я попытаюсь сделать возврат в анонимной функции, я получу тот же результат, как если бы я возвратил его непосредственно из onreadystatechange: var unasigned

2 ответа

Решение

К сожалению, поскольку процесс определения того, используется ли имя пользователя, является асинхронным, невозможно просто вернуть значение true или же false из вызова функции. Что вы можете сделать, так это настроить нечто похожее на то, что у вас есть сейчас (обратные вызовы), используя языковые функции, специально предназначенные для этой конкретной цели.

Обещание является одной из этих функций.

Использование будет выглядеть примерно так:

function username_is_available(username) {
    return new Promise(resolve => {
        get_data("username", username, resolve);
    });
}

function get_data(data_type, data, fn) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            fn(xmlhttp.responseText == "true");
        }
    };
    xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
    xmlhttp.send();
}

// Usage:
username_is_available("Ivan").then(available => {
    let text = available ? "Available" : "Taken";
    document.getElementById("username_err").innerHTML = text;
});

Это зависит от availablity.php возврате true а также false как текст, который преобразуется в логическое значение до resolve называется.

В будущем, когда ES7+ async а также await Директивы доступны, использование обещания будет так же просто, как это (обратите внимание на await ключевое слово):

let available = await username_is_available("Ivan");
let text = available ? "Available" : "Taken";
document.getElementById("username_err").innerHTML = text;

Изменить: Если вы не можете использовать ES6 или обещания, он возвращается к хорошим старым обратным вызовам!

function username_is_available(username, callback) {
    get_data("username", username, callback);
}

function get_data(data_type, data, fn) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            fn(xmlhttp.responseText == "true");
        }
    };
    xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
    xmlhttp.send();
}

// Usage:
username_is_available("Ivan", function(available) {
    var text = available ? "Available" : "Taken";
    document.getElementById("username_err").innerHTML = text;
});

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

function Producer() {
    this.listeners = [];
}

Producer.prototype.add = function(listener) {
    this.listeners.push(listener);
};

Producer.prototype.remove = function(listener) {
    var index = this.listeners.indexOf(listener);
    this.listeners.splice(index, 1);
};

Producer.prototype.notify = function(message) {
    this.listeners.forEach(function(listener) {
        listener.update(message);
    });
};

var notifier = new Producer;

function get_data(data_type, data) {
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function () {
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
            notifier.notify(xmlhttp.responseText);
        }
    };
    xmlhttp.open("GET", "availability.php?" + data_type + "=" + data, true);
    xmlhttp.send();
}

var username_is_available = {
    update: function(returned_value) {
        var username = document.getElementById('username').value;
        if (returned_value == 'true') {
            document.getElementById('username_err').innerHTML = 'Taken';
        } else {
            document.getElementById('username_err').innerHTML = 'Available';
        };        
    }
}

notifier.add(username_is_available);
get_data("username", username);

Обратите внимание Producer код можно использовать повторно, вы должны создать новый экземпляр для других запросов ajax/observable.

Другие вопросы по тегам