Ваш код должен быть чем-то вроде этого:
function foo() {
var httpRequest = new XMLHttpRequest();
httpRequest.open('GET', "/echo/json");
httpRequest.send();
return httpRequest.responseText;
}
var result = foo(); // always ends up being 'undefined'
Феликс Клинг отлично справился с написанием ответа для людей, использующих jQuery для AJAX, я решил предоставить альтернативу для людей, которые этого не делают.
( Примечание. используя новый API fetch
, угловые или обещания, я добавил еще один ответ ниже )
Это краткое резюме «Объяснение проблемы» из другого ответа, если вы не уверены, прочитав это, прочитайте это.
A в AJAX означает асинхронность. Это означает, что отправка запроса (или, скорее, получение ответа) вынимается из обычного потока выполнения. В вашем примере .send
немедленно возвращается, а следующий оператор return result;
выполняется до того, как функция, которую вы передали, когда был вызван обратный вызов success
.
Это означает когда вы возвращаетесь, слушатель, который вы определили, еще не выполнил, что означает, что возвращаемое вами значение не было определено.
Вот простая аналогия
function getFive(){
var a;
setTimeout(function(){
a=5;
},10);
return a;
}
Возвращаемое значение a
- undefined
так как часть a=5
еще не выполнена. AJAX действует так, вы возвращаете значение до того, как сервер получил возможность сообщить вашему браузеру, что это за значение.
Одним из возможных решений этой проблемы является код повторно активно , сообщая вашей программе, что делать, когда расчет завершен.
function onComplete(a){ // When the code completes, do this
alert(a);
}
function getFive(whenDone){
var a;
setTimeout(function(){
a=5;
whenDone(a);
},10);
}
Это называется CPS . В основном, мы передаем getFive
действие, которое необходимо выполнить, когда оно завершается, мы сообщаем нашему кодексу, как реагировать, когда событие завершается (например, наш вызов AJAX или в этом случае время ожидания).
Использование будет:
getFive(onComplete);
Который должен предупредить «5» на экране. (Fiddle) .
Существуют два способа решения этой проблемы:
Что касается синхронного AJAX, не делайте этого! Ответ Феликса вызывает некоторые веские аргументы в пользу того, почему это плохая идея. Подводя итог, он заморозит браузер пользователя, пока сервер не вернет ответ и не создаст очень плохой пользовательский интерфейс. Вот еще краткое резюме из MDN о том, почему:
XMLHttpRequest поддерживает как синхронную, так и асинхронную связь. В общем, однако, асинхронные запросы должны быть предпочтительнее синхронных запросов по причинам производительности.
Короче говоря, синхронные запросы блокируют выполнение кода ... ... это может вызвать серьезные проблемы ...
Если вы имеете , вы можете передать флаг: Вот как это сделать:
var request = new XMLHttpRequest(); request.open('GET', 'yourURL', false); // `false` makes the request synchronous request.send(null); if (request.status === 200) {// That's HTTP for 'ok' console.log(request.responseText); }
2. Код реструктуризации
Пусть ваша функция принимает обратный вызов. В примере код
foo
может быть сделан для принятия обратного вызова. Мы сообщим нашему кодексу, как отреагировали , когдаfoo
завершает работу.Итак:
var result = foo(); // code that depends on `result` goes here
Становится:
foo(function(result) { // code that depends on `result` });
Здесь мы передали анонимную функцию, но мы могли бы так же легко передать ссылку на существующую , чтобы он выглядел следующим образом:
function myHandler(result) { // code that depends on `result` } foo(myHandler);
Для получения дополнительной информации о том, как выполняется этот вид обратного вызова, проверьте ответ Felix.
Теперь давайте определим сам foo, чтобы действовать соответственно
function foo(callback) { var httpRequest = new XMLHttpRequest(); httpRequest.onload = function(){ // when the request is loaded callback(httpRequest.responseText);// we're calling our method }; httpRequest.open('GET', "/echo/json"); httpRequest.send(); }
Теперь мы сделали нашу функцию foo принять действие, которое будет выполняться, когда AJAX завершится успешно, мы можем продолжить это, проверив, не является ли статус ответа не 200 и действует соответственно (создайте обработчик сбоя и т. д.). Эффективное решение нашей проблемы.
Если вам все еще трудно понять это , прочитайте руководство по началу работы AJAX в MDN.
Поскольку матовый указывает, Alamofire возвращает данные асинхронно через шаблон completionHandler
, поэтому вы должны сделать то же самое. Вы можете не просто return
значение сразу, но вместо этого вы хотите использовать возвращаемый тип Void
и вместо этого использовать шаблон закрытия обработчика завершения.
В Swift 3 и Alamofire 4 это может выглядеть так:
func getOrders(completionHandler: @escaping (NSDictionary?, Error?) -> ()) {
makeCall("orders", completionHandler: completionHandler)
}
func makeCall(_ section: String, completionHandler: @escaping (NSDictionary?, Error?) -> ()) {
let params = ["consumer_key":"key", "consumer_secret":"secret"]
Alamofire.request("\(apiUrl)/\(apiEndPoint + section)", parameters: params)
.authenticate(user: consumerKey, password: consumerSecret)
.responseJSON { response in
switch response.result {
case .success(let value):
completionHandler(value as? NSDictionary, nil)
case .failure(let error):
completionHandler(nil, error)
}
}
}
Затем, когда вы хотите вызвать его, вы используете этот параметр completionHandler
(если хотите, в закрытии закрытия):
let api = API()
api.getOrders() { responseObject, error in
// use responseObject and error here
print("responseObject = \(responseObject); error = \(error)")
return
}
// but don't try to use them here
Чтобы разобрать json с помощью Swifty JSON, вот как я это делаю. Для @Jenita _Alice4Real
func uploadScans(parameters: [String: AnyObject], completionHandler: (AnyObject?, NSError?) -> ()) {
makePostCall(CommonFunctions().getSaveSKUDataUrl(), parameters: parameters,completionHandler: completionHandler)
}
func makePostCall(url: String, parameters: [String: AnyObject], completionHandler: (AnyObject?, NSError?) -> ()) {
Alamofire.request(.POST, url, parameters: parameters)
.responseJSON { response in
switch response.result {
case .Success(let value):
completionHandler(value, nil)
case .Failure(let error):
completionHandler(nil, error)
}
}
}
uploadScans(params) { responseObject, error in
let json = JSON(responseObject!)
}
Из Alamofire README (выделено мной):
Работа в сети Alamofire выполняется асинхронно. Асинхронное программирование может быть источником разочарования для программистов, незнакомых с концепцией, но есть очень веские причины для этого.
Вместо блокировки выполнения для ожидания ответа с сервера обратный вызов указанный для обработки ответа после его получения. Результат запроса доступен только в объеме обработчика ответа. Любое выполнение, зависящее от ответа или данных, полученных от сервера, должно выполняться в обработчике.
blockquote>
Ниже приведен полный поток для выполнения «действия входа» с использованием Alamofire и Swift.
Alamofire v3.3 Swift 2.2 Xcode 7.3
Я использовал GCD и MBProgressHUD для моего собственное удобство. Рефакторинг и использование по своему усмотрению:)
func loginBtnTapped(sender: AnyObject) {
MBProgressHUD.showHUDAddedTo(self.view, animated: true)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let loginInfo : Dictionary<String,AnyObject> = ["email":"abc@g.com","password":"abc123"]
self.loginUser(loginInfo) { responseObject, error in
print("\(responseObject) \n \(error) ")
// Parsing JSON Below
let status = Int(responseObject?.objectForKey("status") as! String)
if status == 1 {
// Login Successfull...Move To New VC
}
else {
print(responseObject?.objectForKey("message"))! as! String)
}
return
}
dispatch_async(dispatch_get_main_queue()) {
MBProgressHUD.hideHUDForView(self.view, animated: true)
}
}
}
func loginUser(parameters:NSDictionary, completionHandler: (NSDictionary?, NSError?) -> ()) {
self.postRequest("http://qa.company.com/project/index.php/user/login",
paramDict: parameters as? Dictionary<String, AnyObject>,
completionHandler: completionHandler)
}
func postRequest(urlString: String, paramDict:Dictionary<String, AnyObject>? = nil,
completionHandler: (NSDictionary?, NSError?) -> ()) {
Alamofire.request(.POST, urlString, parameters: paramDict)
.responseJSON { response in
switch response.result {
case .Success(let JSON):
completionHandler(JSON as? NSDictionary, nil)
case .Failure(let error):
completionHandler(nil, error)
}
}
}
xCode 9.1, Swift 4
Особенности:
Возврат данных с использованием
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Data1.searchRequest(term: "jack johnson") { json, error in
print(error ?? "nil")
print(json ?? "nil")
print("Update views")
}
}
}
UIViewController
g14]Data1.searchRequest(term: "jack johnson") { json, error in print(error ?? "nil") print(json ?? "nil") print("Update views") }
Полный образец 1
Класс данных
blockquote> blockquote>class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. Data1.searchRequest(term: "jack johnson") { json, error in print(error ?? "nil") print(json ?? "nil") print("Update views") } } }
Образец 2
Возвращает данные с использованием делегата
// .... var data = Data2() data.delegate = self data.searchRequest(term: "jack johnson") // .... extension ViewController: Data2Delegate { func searchRequest(response json: [String : Any]?, error: Error?) { print(error ?? "nil") print(json ?? "nil") print("Update views") } }
Полный образец 2
Класс данных
blockquote>import Alamofire protocol Data2Delegate: class { func searchRequest(response json: [String: Any]?, error: Error?) } class Data2 { fileprivate let queue = DispatchQueue(label: "requests.queue", qos: .utility) fileprivate let mainQueue = DispatchQueue.main weak var delegate: Data2Delegate? fileprivate func make(request: DataRequest, closure: @escaping (_ json: [String: Any]?, _ error: Error?)->()) { request.responseJSON(queue: queue) { response in // print(response.request ?? "nil") // original URL request // print(response.response ?? "nil") // HTTP URL response // print(response.data ?? "nil") // server data //print(response.result ?? "nil") // result of response serialization switch response.result { case .failure(let error): self.mainQueue.async { closure(nil, error) } case .success(let data): self.mainQueue.async { closure((data as? [String: Any]) ?? [:], nil) } } } } func searchRequest(term: String) { let request = Alamofire.request("https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))") make(request: request) { json, error in self.delegate?.searchRequest(response: json, error: error) } } }
UIViewController
blockquote>import UIKit class ViewController: UIViewController { private var data = Data2() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. data.delegate = self data.searchRequest(term: "jack johnson") } } extension ViewController: Data2Delegate { func searchRequest(response json: [String : Any]?, error: Error?) { print(error ?? "nil") print(json ?? "nil") print("Update views") } }
Образец 3
Возвращает данные с помощью PromiseKit
_ = data.searchRequest(term: "jack johnson").then { response in print(response.error ?? "nil") print(response.json ?? "nil") print("Update views") return .void }
Полный образец 3
Импорт класса данных Импорт Alamofire PromiseKit
blockquote>class Data3 { fileprivate let queue = DispatchQueue(label: "requests.queue", qos: .utility) fileprivate let mainQueue = DispatchQueue.main fileprivate func make(request: DataRequest) -> Promise<(json:[String: Any]?, error: Error?)> { return Promise { fulfill, reject in request.responseJSON(queue: queue) { response in // print(response.request ?? "nil") // original URL request // print(response.response ?? "nil") // HTTP URL response // print(response.data ?? "nil") // server data //print(response.result ?? "nil") // result of response serialization switch response.result { case .failure(let error): self.mainQueue.async { fulfill((nil, error)) } case .success(let data): self.mainQueue.async { fulfill(((data as? [String: Any]) ?? [:], nil)) } } } } } func searchRequest(term: String) -> Promise<(json:[String: Any]?, error: Error?)> { let request = Alamofire.request("https://itunes.apple.com/search?term=\(term.replacingOccurrences(of: " ", with: "+"))") return make(request: request) } } extension AnyPromise { class var void: AnyPromise { return AnyPromise(Promise<Void>()) } }
UIViewController
blockquote>import UIKit import PromiseKit class ViewController: UIViewController { private var data = Data3() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. _ = data.searchRequest(term: "jack johnson").then { response in print(response.error ?? "nil") print(response.json ?? "nil") print("Update views") return .void } } }