web-gelistirme-sc.com

Zaman uyumsuz bir çağrının yanıtını nasıl döndürürüm?

Bir Ajax isteği yapan foo işlevim var. Cevabı foo adresinden nasıl iade edebilirim?

Değeri success callback işlevinden döndürmeyi ve işlev içindeki yerel bir değişkene yanıt vermeyi ve bunu döndürmeyi denedim, ancak bu yöntemlerden hiçbiri gerçekte yanıtı döndürmedi.

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });

    return result;
}

var result = foo(); // It always ends up being `undefined`.
4904
Felix Kling

→ Farklı örneklerle eşzamansız davranışın daha genel bir açıklaması için, lütfen bakınızDeğişkenim neden bir işlev içinde değiştirdikten sonra değişmiyor? - Eşzamansız kod başvurusu

→ Sorunu zaten anladıysanız, aşağıdaki olası çözümlere atlayın.

Sorun

Ain Ajax , asenkron anlamına gelir. Bu, isteğin gönderilmesi (veya daha doğrusu yanıtın alınması) normal uygulama akışından alındığı anlamına gelir. Örnekte, $.ajax derhal geri döner ve sonraki ifade, return result;, success callback olarak ilettiğiniz işlevden önce çağrılır.

Senkronize ve asenkron akış arasındaki farkı daha net bir şekilde ortaya çıkaran umarım bir benzetme:

Senkron

Bir arkadaşınıza bir telefon görüşmesi yaptığınızı ve ondan sizin için bir şey aramasını istediğinizi hayal edin. Biraz zaman alabilir, ancak arkadaşınız size gereken cevabı verene kadar telefonda bekleyin ve uzaya bakın.

Aynısı "normal" kod içeren bir işlev çağrısı yaptığınızda oluyor:

function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

findItem işlevinin yürütülmesi uzun zaman alabilir, ancak var item = findItem(); işlevinden sonra gelen herhangi bir kod, işlev sonuç verene kadar wait işlevine sahip olmalıdır.

Eşzamanlı olmayan

Aynı sebepten dolayı arkadaşını tekrar ara. Fakat bu sefer ona acelesi olduğunu ve cep telefonunda seni geri ara yapması gerektiğini söylüyorsun. Telefonu kapat, evden ayrıl ve ne yapmayı planlıyorsan onu yap. Arkadaşınız sizi geri aradığında, size verdiği bilgilerle ilgileniyorsunuz.

Ajax isteğinde bulunduğunda olan tam olarak bu.

findItem(function(item) {
    // Do something with item
});
doSomethingElse();

Yanıtı beklemek yerine, yürütme derhal devam eder ve Ajax çağrısından sonraki ifade yürütülür. Sonunda yanıtı almak için, yanıt alındıktan sonra çağrılacak bir işlev sağlarsınız, bir geri arama (bir şey fark eder misiniz? Geri arama?). Bu çağrıdan sonra gelen herhangi bir ifade, geri arama çağrılmadan önce gerçekleştirilir.


Çözüm (ler)

JavaScript'in asenkron yapısını kucakla! Bazı eşzamansız işlemler eşzamanlı meslektaşları sağlasa da ("Ajax" da), genellikle bunları tarayıcı bağlamında kullanmaktan kaçınır.

Neden soruyorsun kötü?

JavaScript, tarayıcının UI iş parçacığında çalışır ve uzun süren herhangi bir işlem kullanıcı arabirimini kilitleyerek yanıt vermez. Ek olarak, JavaScript için yürütme süresinde bir üst sınır vardır ve tarayıcı kullanıcıdan yürütmeye devam edip etmeyeceğini soracaktır.

Bütün bunlar gerçekten kötü bir kullanıcı deneyimi. Kullanıcı her şeyin yolunda olup olmadığını söyleyemez. Ayrıca, yavaş bağlantıya sahip kullanıcılar için bu etki daha da kötüleşecektir.

Aşağıda, her biri üst üste inşa edilmiş üç farklı çözüme bakacağız:

  • async/await ile vaat ediyor (ES2017 +, bir aktarıcı veya rejeneratör kullanıyorsanız, eski tarayıcılarda kullanılabilir)
  • Geri aramalar (düğümde popüler)
  • then() ile verilen sözler (ES2015 +, çok sayıda söz veren kütüphaneden birini kullanıyorsanız, eski tarayıcılarda kullanılabilir)

Üçü de geçerli tarayıcılarda ve düğüm 7 + 'da kullanılabilir.


ES2017 +: async/await ile vaat ediyor

2017'de piyasaya sürülen ECMAScript sürümü, zaman uyumsuz işlevler için sözdizimi düzeyinde destek'yi tanıttı. async ve await yardımı ile, "senkronize bir stilde" asenkronize yazabilirsiniz. Kod hala eşzamansızdır ancak okunması/anlaşılması daha kolaydır.

async/await vaatlerin üstüne kuruludur: async işlevi her zaman bir söz verir. await bir vaadi "çözer" ve ya vaadin çözüldüğü değerle sonuçlanır ya da söz reddedilirse bir hata yapar.

Önemli: Bir await işlevinde yalnızca async işlevini kullanabilirsiniz. Şu anda, üst düzey await henüz desteklenmiyor, bu nedenle bir async bağlamı başlatmak için bir async IIFE ( Hemen Çağrılan İşlev İfadesi ) yapmanız gerekebilir.

MDN'de async ve await hakkında daha fazla bilgi edinebilirsiniz.

İşte yukarıdaki gecikmenin üstüne çıkan bir örnek:

// Using 'superagent' which will return a promise.
var superagent = require('superagent')

// This is isn't declared as `async` because it already returns a promise
function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}


async function getAllBooks() {
  try {
    // GET a list of book IDs of the current user
    var bookIDs = await superagent.get('/user/books');
    // wait for 3 seconds (just for the sake of this example)
    await delay();
    // GET information about each book
    return await superagent.get('/books/ids='+JSON.stringify(bookIDs));
  } catch(error) {
    // If any of the awaited promises was rejected, this catch block
    // would catch the rejection reason
    return null;
  }
}

// Start an IIFE to use `await` at the top level
(async function(){
  let books = await getAllBooks();
  console.log(books);
})();

Geçerli tarayıcı ve düğüm sürümleri async/await özelliğini destekler. Ayrıca, eski ortamları kodunuzu ES5'e rejeneratörü (veya Babel gibi rejeneratör kullanan araçlar) yardımıyla dönüştürerek de destekleyebilirsiniz.


İşlevlerin callbacks kabul etmesine izin ver

Geri çağırma, basitçe başka bir işleve geçen bir işlevdir. Diğer işlev, hazır olduğunda, geçirilen işlevi çağırabilir. Zaman uyumsuz bir işlem bağlamında, zaman uyumsuz işlem yapıldığında geri çağrı çağrılır. Genellikle, sonuç geri aramaya iletilir.

Soru örneğinde, foo işlevinin bir geri aramayı kabul etmesini ve success geri arama olarak kullanmasını sağlayabilirsiniz. Yani bu

var result = foo();
// Code that depends on 'result'

olur

foo(function(result) {
    // Code that depends on 'result'
});

Burada "inline" fonksiyonunu tanımladık fakat herhangi bir fonksiyon referansını geçebilirsiniz:

function myCallback(result) {
    // Code that depends on 'result'
}

foo(myCallback);

foo'ın kendisi şu şekilde tanımlanır:

function foo(callback) {
    $.ajax({
        // ...
        success: callback
    });
}

callback, onu çağırdığımızda foo öğesine geçirdiğimiz işlevi ifade eder ve basitçe success öğesine geçiririz. Yani Ajax isteği başarılı olduğunda, $.ajaxcallback öğesini arayacak ve geri çağrıya verilen yanıtı iletecektir (ki bu geri dönüşü bu şekilde tanımladığımızdan dolayı result ile belirtilebilir).

Yanıtı geri aramaya geçirmeden önce de işleyebilirsiniz:

function foo(callback) {
    $.ajax({
        // ...
        success: function(response) {
            // For example, filter the response
            callback(filtered_response);
        }
    });
}

Geri aramaları kullanarak kod yazmak göründüğünden daha kolaydır. Ne de olsa, tarayıcıdaki JavaScript büyük oranda olay odaklı (DOM olayları). Ajax yanıtını almak bir olaydan başka bir şey değildir.
Üçüncü taraf kodlarıyla çalışmak zorunda kaldığınızda zorluklar ortaya çıkabilir, ancak çoğu sorun yalnızca uygulama akışını düşünerek çözülebilir.


ES2015 +: ardından () ile vaat ediyor

Promise API , ECMAScript 6'nın (ES2015) yeni bir özelliğidir, ancak/ tarayıcı desteği iyi zaten var. Standart Promises API'sini uygulayan ve asenkron işlevlerin kullanımını ve bileşimini kolaylaştırmak için ek yöntemler sağlayan birçok kitaplık da vardır (örneğin bluebird ).

Sözler, future değerleri için kapsayıcıdır. Söz verdiğimiz değeri aldığında (çözüldü) veya iptal edildiğinde (reddedildi), bu değere erişmek isteyen tüm "dinleyicilerini" bildirir.

Düz geri aramalara göre avantajı, kodunuzu ayırmanıza izin vermesi ve oluşturması daha kolay olmasıdır.

İşte bir söz kullanmanın basit bir örneği:

function delay() {
  // `delay` returns a promise
  return new Promise(function(resolve, reject) {
    // Only `delay` is able to resolve or reject the promise
    setTimeout(function() {
      resolve(42); // After 3 seconds, resolve the promise with value 42
    }, 3000);
  });
}

delay()
  .then(function(v) { // `delay` returns a promise
    console.log(v); // Log the value once it is resolved
  })
  .catch(function(v) {
    // Or do something else if it is rejected 
    // (it would not happen in this example, since `reject` is not called).
  });

Ajax çağrımıza uygulandığında şöyle sözler kullanabiliriz:

function ajax(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open('GET', url);
    xhr.send();
  });
}

ajax("/echo/json")
  .then(function(result) {
    // Code depending on result
  })
  .catch(function() {
    // An error occurred
  });

Teklif vaat eden tüm avantajları açıklamak, bu cevabın kapsamı dışındadır, ancak yeni bir kod yazarsanız, bunları ciddiye almalısınız. Kodunuzun mükemmel bir soyutlamasını ve ayrılmasını sağlarlar.

Vaatler hakkında daha fazla bilgi: HTML5 rocks - JavaScript Promises

Yan not: jQuery'nin ertelenmiş nesneleri

Ertelenen nesneler jQuery'nin vaatlerin özel uygulamasıdır (Promise API standardize edilmeden önce). Neredeyse vaat ettikleri gibi davranırlar, ancak biraz farklı bir API gösterir.

Her jQuery metodunun Ajax metodu, fonksiyonunuzdan sadece geri dönebileceğiniz bir "ertelenmiş nesne" (aslında ertelenmiş bir nesnenin vaadi) döndürür:

function ajax() {
    return $.ajax(...);
}

ajax().done(function(result) {
    // Code depending on result
}).fail(function() {
    // An error occurred
});

Yan not: Promise gotchas

Vaat edilen ve ertelenen nesnelerin gelecekteki bir değer için sadece container olduğunu, değerin kendisi olmadığını unutmayın. Örneğin, aşağıdakilere sahip olduğunuzu varsayalım:

function checkPassword() {
    return $.ajax({
        url: '/password',
        data: {
            username: $('#username').val(),
            password: $('#password').val()
        },
        type: 'POST',
        dataType: 'json'
    });
}

if (checkPassword()) {
    // Tell the user they're logged in
}

Bu kod yukarıdaki eşzamansız sorunları yanlış anlıyor. Spesifik olarak, $.ajax(), sunucunuzdaki '/ şifre' sayfasını kontrol ederken kodu dondurmaz; sunucuya bir istek gönderir ve beklerken, sunucudan gelen yanıtı değil hemen bir jQuery Ajax Deferred nesnesini döndürür. Bu, if deyiminin daima bu Deferred nesnesini alacağı, true olarak kabul edeceği ve kullanıcı giriş yapmış gibi devam edeceği anlamına gelir.

Ancak düzeltme kolaydır:

checkPassword()
.done(function(r) {
    if (r) {
        // Tell the user they're logged in
    } else {
        // Tell the user their password was bad
    }
})
.fail(function(x) {
    // Tell the user something bad happened
});

Tavsiye edilmiyor: Senkron "Ajax" çağrıları

Bahsettiğim gibi, bazı (!) Zaman uyumsuz işlemlerin zaman uyumlu muadilleri var. Kullanımlarını savunmuyorum, ancak bütünlük uğruna, nasıl senkronize bir çağrı yapacağınız:

JQuery olmadan

Doğrudan bir XMLHTTPRequest / nesnesi kullanıyorsanız, false öğesini üçüncü değişken olarak .open öğesine iletin.

jQuery

jQuery kullanıyorsanız, async seçeneğini false olarak ayarlayabilirsiniz. Bu seçeneğin, jQuery 1.8'den bu yana kullanım dışı olduğuna dikkat edin. O zaman hala bir success geri çağrısı kullanabilir veya jqXHR nesnesinin responseText özelliğine erişebilirsiniz

function foo() {
    var jqXHR = $.ajax({
        //...
        async: false
    });
    return jqXHR.responseText;
}

$.get, $.getJSON, vb. Gibi başka bir jQuery Ajax yöntemi kullanıyorsanız, onu $.ajax olarak değiştirmelisiniz (çünkü yapılandırma parametrelerini yalnızca $.ajax olarak geçirebilirsiniz).

Başlar! Senkronize JSONP istek yapılamaz. JSONP, doğası gereği, her zaman asenkrondir (bu seçeneği düşünmemek için bir neden daha).

5203
Felix Kling

Kodunuzda jQuery kullanarak değil kullanıyorsanız, bu cevap tam size göre

Kodunuz, bu satırlar boyunca bir şey olmalıdır:

function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // always ends up being 'undefined'

Felix Kling, AJAX için jQuery kullanan kişilere cevap yazma konusunda iyi bir iş yaptı, olmayan insanlara bir alternatif sunmaya karar verdim.

( Not, yeni fetch API'sini kullananlar için, Angular veya aşağıdakilere başka bir cevap ekledim:


Neyle yüzleşiyorsun

Bu, diğer cevabın "Sorunun açıklaması" nın kısa bir özetidir, bunu okuduktan sonra emin değilseniz, bunu okuyun.

Ain AJAX, asenkron anlamına gelir. Bu, isteğin gönderilmesi (veya daha doğrusu yanıtın alınması) normal uygulama akışından alındığı anlamına gelir. Örneğinizde, .send derhal geri döner ve sonraki ifade, return result;, success callback olarak çağrılan işlevden önce çağrılır.

Bu, geri döndüğünüzde, tanımladığınız dinleyici henüz çalışmadı, bu da döndürdüğünüz değerin tanımlanmadığı anlamına gelir.

İşte basit bir benzetme

function getFive(){ 
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

(Fiddle)

Döndürülen a değeri, a=5 bölümü henüz yürütülmediğinden undefined değeridir. AJAX böyle davranır, sunucu tarayıcınıza bu değerin ne olduğunu söyleme şansını yakalamadan önce değeri döndürürsünüz.

Bu sorunun olası bir çözümü, hesaplama tamamlandığında programınıza ne yapacağınızı söyleyerek yeniden aktif olarak kodunu vermektir.

function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){ 
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}

Buna CPS adı verilir. Temel olarak, getFive__ tamamlandığında gerçekleştirilecek bir eylemden geçiyoruz, kodumuza bir etkinlik tamamlandığında nasıl tepki vereceğimizi söylüyoruz (AJAX çağrısı veya bu durumda zaman aşımı gibi).

Kullanım olurdu:

getFive(onComplete);

Hangi "5" ekrana uyarmalıdır. (Keman) .

Muhtemel çözümler

Bunu çözmenin temelde iki yolu var:

  1. AJAX çağrısını senkronize edin (bunu SJAX olarak adlandıralım).
  2. Geri aramalar ile düzgün çalışması için kodunuzu yeniden yapılandırın.

1. Senkron AJAX - Yapma !!

Senkron AJAX'a gelince, yapma! Felix'in cevabı, neden kötü bir fikir olduğu konusunda zorlayıcı tartışmalara yol açıyor. Özetlemek gerekirse, sunucu yanıtı döndürene ve çok kötü bir kullanıcı deneyimi yaratana kadar kullanıcının tarayıcısını dondurur. İşte MDN'den neden alındığı hakkında kısa bir özet:

XMLHttpRequest, hem senkron hem de asenkron iletişimleri destekler. Ancak, genel olarak, zaman uyumsuz istekler, performans nedenlerinden dolayı senkronize istekler için tercih edilmelidir.

Kısacası, senkronize istekler kodun yürütülmesini engeller ... ... bu ciddi sorunlara neden olabilir ...

Bunu yapmak için sahip kullanıyorsanız, bir bayrağı iletebilirsiniz: İşte böyle:

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. Yeniden yapılandırma kodu

İşleviniz bir geri aramayı kabul etsin. Örnek kodda foo bir geri aramayı kabul etmek için yapılabilir. Kodumuza foo işlemi tamamlandığında nasıl reaksiyon yapacağımızı söyleyeceğiz.

Yani:

var result = foo();
// code that depends on `result` goes here

Oluyor:

foo(function(result) {
    // code that depends on `result`
});

Burada anonim bir işlevi geçtik, ancak mevcut bir işleve bir referansı kolayca ileterek şöyle görünmesini sağladık:

function myHandler(result) {
    // code that depends on `result`
}
foo(myHandler);

Bu tür bir geri arama tasarımının nasıl yapıldığına dair daha fazla ayrıntı için Felix'in cevabını kontrol edin.

Şimdi, foo'nun kendisini buna göre davranmasını tanımlayalım.

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();
}

(keman)

Şimdi foo işlevimizin AJAX başarıyla tamamlandığında çalıştırılacak bir eylemi kabul etmesini sağladık, yanıt durumunun 200 olup olmadığını kontrol edip buna göre davranarak (bir başarısız işleyici ve benzeri oluşturarak) bunu daha da genişletebiliriz. Sorunumuzu etkin bir şekilde çözme.

Bunu anlamakta hala zorlanıyorsanız / MDN'deki AJAX başlangıç ​​kılavuzunu okuyun.

1002

XMLHttpRequest 2 (her şeyden önce Benjamin Gruenbaum & Felix Kling

Eğer jQuery kullanmıyorsanız ve modern tarayıcılarda ve ayrıca mobil tarayıcılarda çalışan Nice kısa bir XMLHttpRequest 2 istiyorsanız, bu şekilde kullanmanızı öneririm:

function ajax(a, b, c){ // URL, callback, just a placeholder
  c = new XMLHttpRequest;
  c.open('GET', a);
  c.onload = b;
  c.send()
}

Gördüğün gibi:

  1. Listelenen diğer tüm işlevlerden daha kısa.
  2. Geri arama doğrudan ayarlanır (bu yüzden fazladan fazladan kapatma gerekmez).
  3. Yeni aşırı yükü kullanır (bu nedenle, okuma sistemi durumunu ve durumunu kontrol etmeniz gerekmez)
  4. XMLHttpRequest 1'i sinirlendiren, hatırlamadığım başka durumlar da var.

Bu Ajax çağrısının yanıtını almanın iki yolu vardır (üçü XMLHttpRequest var adını kullanarak):

En basit:

this.response

Veya bazı nedenlerden dolayı, bir sınıfa geri çağırma bind() eğer:

e.target.response

Örnek:

function callback(e){
  console.log(this.response);
}
ajax('URL', callback);

Veya (yukarıdakilerden daha iyisi anonim işlevler her zaman bir problemdir):

ajax('URL', function(e){console.log(this.response)});

Daha kolay değil.

Şimdi bazı insanlar muhtemelen onreadystatechange'i veya hatta XMLHttpRequest değişken adını kullanmanın daha iyi olacağını söyleyecekler. Bu yanlış.

Çıkış XMLHttpRequest gelişmiş özellikleri

Tüm * modern tarayıcıları destekledi. XMLHttpRequest 2 var olduğundan beri bu yaklaşımı kullandığımı doğrulayabilirim. Kullandığım tüm tarayıcılarda hiçbir zaman sorun yaşamadım.

onreadystatechange yalnızca başlıkları 2 durumuna almak istiyorsanız kullanışlıdır.

XMLHttpRequest değişkeni adını kullanmak, onu kaybettiğiniz başka bir büyük hatadır, aksi halde kaybettiğiniz yük/oreadystatechange kapanışları içinde geri aramayı gerçekleştirmeniz gerekir.


Şimdi post ve FormData kullanarak daha karmaşık bir şey istiyorsanız, bu işlevi kolayca genişletebilirsiniz:

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.send(d||null)
}

Yine ... çok kısa bir işlev, ama alma ve gönderme.

Kullanım örnekleri:

x(url, callback); // By default it's get so no need to set
x(url, callback, 'post', {'key': 'val'}); // No need to set post data

Veya tam bir form elemanı iletin (document.getElementsByTagName('form')[0]):

var fd = new FormData(form);
x(url, callback, 'post', fd);

Veya bazı özel değerler ayarlayın:

var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);

Gördüğünüz gibi senkronizasyon yapmadım ... bu kötü bir şey.

Bunu söyledikten sonra ... neden kolay yoldan yapmıyorsun?


Yorumda da belirtildiği gibi, && senkronize hata kullanımı tamamen cevabı işaret ediyor. Ajax'ı uygun şekilde kullanmak için güzel bir kısa yol hangisi?

Hata işleyicisi

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val}, placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.onerror = error;
  c.send(d||null)
}

function error(e){
  console.log('--Error--', this.type);
  console.log('this: ', this);
  console.log('Event: ', e)
}
function displayAjax(e){
  console.log(e, this);
}
x('WRONGURL', displayAjax);

Yukarıdaki komut dosyasında, statik olarak tanımlanmış bir hata işleyiciniz vardır, böylece işlevden ödün vermez. Hata işleyicisi diğer işlevler için de kullanılabilir.

Ancak gerçekten bir hata almak için only way, yanlış bir URL yazmaktır, bu durumda her tarayıcı bir hata atar.

Hata işleyicileri, eğer özel başlıklar belirlerseniz, responseType'ı bir dizi arabellek ya da her neyse blob olarak ayarlayın ...

'POSTAPAPAP' öğesini yöntem olarak geçseniz bile, hata atmaz.

'Fdggdgilfdghfldj' formdata olarak geçseniz bile, hata atmaz.

İlk durumda, hata this.statusText olarak Method not Allowed altındaki displayAjax() içindedir.

İkinci durumda, sadece çalışır. Doğru gönderi verilerinden geçip geçmediğinizi sunucu tarafında kontrol etmeniz gerekir.

etki alanları arası izin verilmiyor, otomatik olarak hata veriyor.

Hata yanıtında hata kodu yoktur.

Hatalı olarak ayarlanan sadece this.type var.

Hatalar üzerinde tamamen kontrolünüz yoksa neden bir hata işleyicisi ekleyin? Hataların çoğu, bunun içinde geri arama işlevinde displayAjax() işlevinde döndürülür.

Yani: URL'yi düzgün bir şekilde kopyalayıp yapıştırabiliyorsanız hata kontrollerine gerek yok. ;)

PS: İlk test olarak x ('x', displayAjax) yazdım ... ve tamamen bir yanıt aldım ... ??? Ben de HTML'nin bulunduğu klasörü kontrol ettim ve bir 'x.xml' adı verilen dosya. XMLHttpRequest 2 dosyanızın uzantısını unutsanız bile. Ben yedim


Bir dosyayı senkronize oku

Yapma bunu.

Bir süre tarayıcıyı engellemek istiyorsanız senkronize bir Nice big .txt dosyası yükleyin.

function omg(a, c){ // URL
  c = new XMLHttpRequest;
  c.open('GET', a, true);
  c.send();
  return c; // Or c.response
}

Şimdi yapabilirsin

 var res = omg('thisIsGonnaBlockThePage.txt');

Bunu asenkronize olmayan bir şekilde yapmanın başka bir yolu yoktur. (Evet, setTimeout döngüsüyle ... ama cidden?)

Başka bir nokta şudur: API'larla veya yalnızca kendi listenizin dosyalarıyla çalışıyorsanız veya her istek için farklı işlevler kullanıyorsanız ...

Yalnızca her zaman aynı XML/JSON'u yüklediğiniz bir sayfanız veya yalnızca bir işleve ihtiyacınız olan bir sayfa varsa. Bu durumda, biraz Ajax işlevini değiştirin ve b'yi özel işlevinizle değiştirin.


Yukarıdaki işlevler temel kullanım içindir.

Fonksiyonu genişletmek istiyorsanız ...

Evet yapabilirsin.

Çok fazla API kullanıyorum ve her HTML sayfasına entegre ettiğim ilk işlevlerden biri bu cevabın ilk Ajax işlevi, yalnızca GET ile ...

Ancak XMLHttpRequest 2 ile birçok şey yapabilirsiniz:

Bir indirme yöneticisi yaptım (her iki tarafta da aralıkları özgeçmiş, filereader, dosya sistemi kullanarak), tuval kullanarak çeşitli görüntü düzenleyiciler dönüştürücüler, base64images içeren web SQL veritabanlarını doldurmak ve çok daha fazlası ... Ancak bu durumlarda sadece bunun için bir işlev oluşturmalısınız Amaç ... Bazen bir blob, dizi tamponları gerekir, başlıkları ayarlayabilir, mime tipini geçersiz kılabilir ve çok daha fazlası ...

Fakat buradaki soru, bir Ajax yanıtını nasıl iade edeceğimiz ... (Kolay bir yol ekledim.)

363
cocco

Eğer söz kullanıyorsanız, bu cevap tam size göre.

Bu, AngularJS, jQuery (ertelenmiş), yerel XHR'nin değiştirilmesi (getirme), EmberJS, BackboneJS'in kaydetmesi veya söz veren herhangi bir düğüm kütüphanesi anlamına gelir.

Kodunuz, bu satırlar boyunca bir şey olmalıdır:

function foo() {
    var data;
    // or $.get(...).then, or request(...).then, or query(...).then
    fetch("/echo/json").then(function(response){
        data = response.json();
    });
    return data;
}

var result = foo(); // result is always undefined no matter what.

Felix Kling, jQuery kullanan kişilere AJAX'in geri çağrılarıyla cevap yazmak için iyi bir iş yaptı. Yerel XHR için bir cevabım var. Bu cevap sözlerin ya ön uçta ya da arka uçta genel kullanımı içindir.


Çekirdek sorunu

Tarayıcıda ve NodeJS/io.js ile sunucudaki JavaScript eşzamanlılık modeli asenkron ve reaktif'dir.

Ne zaman söz veren bir yöntem çağırırsanız, thenişleyicileri, her zaman eşzamansız olarak yürütülür - yani, after altlarındaki bir .then işleyicisinde değil.

Bu, dataişlevini döndürürken, tanımladığınız thenişleyicisini henüz çalıştırmadığınız anlamına gelir. Bu da, döndürdüğünüz değerin zaman içinde doğru değere ayarlanmadığı anlamına gelir.

İşte sorun için basit bir benzetme:

    function getFive(){
        var data;
        setTimeout(function(){ // set a timer for one second in the future
           data = 5; // after a second, do this
        }, 1000);
        return data;
    }
    document.body.innerHTML = getFive(); // `undefined` here and not 5

datadeğeri, data = 5 bölümü henüz çalıştırılmadığından, undefineddeğeridir. Muhtemelen bir saniyede gerçekleşecek, fakat o zamana kadar iade edilen değer ile ilgisi yok.

İşlem henüz gerçekleşmediğinden (AJAX, sunucu çağrısı, IO, zamanlayıcı), istek geri döndüğünde değeri geri döndürüyorsunuz, kodunuza bu değerin ne olduğunu söyleme şansınız var.

Bu sorunun olası bir çözümü, hesaplama tamamlandığında programınıza ne yapacağınızı söyleyerek yeniden aktif olarak kodunu vermektir. Sözler, doğada geçici (zamana duyarlı) olarak bunu aktif olarak mümkün kılar.

Sözlerde hızlı özetleme

Bir söz bir zaman içindeki değer'dir. Vaatlerde devlet var, hiçbir değeri olmadan beklemeye başlıyorlar ve buna razı olabiliyorlar:

  • fulfilled işlemin başarıyla tamamlandığını gösterir.
  • reddedildi hesaplamanın başarısız olduğu anlamına gelir.

Bir söz, yalnızca bir kez durumlarını değiştirebilir, bundan sonra daima aynı durumda kalır. Değerlerini çıkarmaya ve hataları ele almaya söz vermek için thenişleyicilerini ekleyebilirsiniz. thenişleyicileri çağrıların zincirlenmesi için izin verir. Sözler, onları geri döndüren API'ler kullanılarak tarafından üretilir . Örneğin, daha modern AJAX yerine fetchveya jQuery'nin $.get dönüş vaatleri.

Bir söz üzerine .then derken ve return'den bir şey söylediğimizde - işlenen değer için bir söz alırız. Başka bir söz verirsek harika şeyler elde ederiz, ama atlarımızı tutalım.

Sözlerle

Yukarıdaki konuyu vaatlerle nasıl çözebileceğimizi görelim. Öncelikle, bir gecikme işlevi oluşturmak için Promise yapıcısını kullanarak yukarıdan vaat eden devletler hakkındaki anlayışımızı gösterelim:

function delay(ms){ // takes amount of milliseconds
    // returns a new promise
    return new Promise(function(resolve, reject){
        setTimeout(function(){ // when the time is up
            resolve(); // change the promise to the fulfilled state
        }, ms);
    });
}

Şimdi setTimeout'u vaatleri kullanmaya dönüştürdükten sonra, saymak için thenişlevini kullanabiliriz:

function delay(ms){ // takes amount of milliseconds
  // returns a new promise
  return new Promise(function(resolve, reject){
    setTimeout(function(){ // when the time is up
      resolve(); // change the promise to the fulfilled state
    }, ms);
  });
}

function getFive(){
  // we're RETURNING the promise, remember, a promise is a wrapper over our value
  return delay(100).then(function(){ // when the promise is ready
      return 5; // return the value 5, promises are all about return values
  })
}
// we _have_ to wrap it like this in the call site, we can't access the plain value
getFive().then(function(five){ 
   document.body.innerHTML = five;
});

Temelde, eşzamanlılık modeli nedeniyle yapamayacağımız değer yerine - yapabileceğimiz bir değer için sarmalayıcı döndürüyoruz unwrapthenile. thenile açabileceğiniz bir kutu gibidir.

Bunu uygulamak

Bu, orijinal API çağrınız için aynı kalır:

function foo() {
    // RETURN the promise
    return fetch("/echo/json").then(function(response){
        return response.json(); // process it inside the `then`
    });
}

foo().then(function(response){
    // access the value inside the `then`
})

Yani bu aynı şekilde çalışır. Zaten eşzamansız çağrılardan değer döndüremediğimizi öğrendik, ancak sözleri kullanabilir ve bunları işleme koymak için zincirleyebiliriz. Şimdi, zaman uyumsuz bir çağrıdan yanıtın nasıl döndürüleceğini biliyoruz.

ES2015 (ES6)

ES6, ortada dönebilen ve sonra bulundukları noktaya devam edebilen işlevler olan jeneratörleri tanıtır. Bu genellikle diziler için kullanışlıdır, örneğin:

function* foo(){ // notice the star, this is ES6 so new browsers/node/io only
    yield 1;
    yield 2;
    while(true) yield 3;
}

Yinelenebilecek 1,2,3,3,3,3,.... dizisi üzerinde yineleyici döndüren bir işlevdir. Bu kendi başına ilginç olsa da ve bir çok olasılık için odayı açıyorsa, belli bir ilginç vaka var.

Ürettiğimiz dizi, sayılar yerine bir eylemler dizisiyse - bir eylem gerçekleştirildiğinde işlevi duraklatabilir ve işlevi sürdürmeden önce onu bekleyebiliriz. Bu yüzden bir sayılar dizisi yerine, future değerleri dizisine ihtiyacımız var - yani: sözler.

Bu biraz zor ama çok güçlü bir numara, senkronize olmayan bir şekilde asenkron kod yazmamıza izin veriyor. Bunu sizin için yapan birkaç "koşucu" var, birini yazmak kısa bir kod satırı ama bu cevabın kapsamı dışında. Burada Bluebird'in Promise.coroutine adresini kullanacağım, ancak coveya Q.async gibi başka paketleyiciler de var.

var foo = coroutine(function*(){
    var data = yield fetch("/echo/json"); // notice the yield
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
});

Bu yöntem, diğer coroutinler'den alabileceğimiz bir söz veriyor. Örneğin:

var main = coroutine(function*(){
   var bar = yield foo(); // wait our earlier coroutine, it returns a promise
   // server call done here, code below executes when done
   var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result
   console.log(baz); // runs after both requests done
});
main();

ES2016 (ES7)

ES7'de, bu daha da standardize edilmiştir, şu anda birkaç teklif var ancak hepsinde awaitvaadi yapabilirsiniz. Bu, yukarıdaki ES6 teklifi için asyncve awaitanahtar kelimelerini ekleyerek sadece "şeker" dir (güzel sözdizimi). Yukarıdaki örneği yapmak:

async function foo(){
    var data = await fetch("/echo/json"); // notice the await
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
}

Hala aynı söz verir :)

292

Ajax'ı yanlış kullanıyorsunuz. Buradaki fikir, herhangi bir şey döndürmesini değil, verileri verileri işleyen bir geri çağırma işlevi adı verilen bir öğeye teslim etmektir.

Yani:

function handleData( responseData ) {

    // Do what you want with the data
    console.log(responseData);
}

$.ajax({
    url: "hi.php",
    ...
    success: function ( data, status, XHR ) {
        handleData(data);
    }
});

Gönderim işleyicisinde herhangi bir şey döndürmek hiçbir şey yapmaz. Bunun yerine verileri vermelisiniz ya da istediğinizi doğrudan başarı fonksiyonunun içinde yapmalısınız.

230
Nic

En basit çözüm, bir JavaScript işlevi oluşturmak ve Ajax success geri çağırma için çağırmaktır.

function callServerAsync(){
    $.ajax({
        url: '...',
        success: function(response) {

            successCallback(response);
        }
    });
}

function successCallback(responseObj){
    // Do something like read the response and show data
    alert(JSON.stringify(responseObj)); // Only applicable to JSON response
}

function foo(callback) {

    $.ajax({
        url: '...',
        success: function(response) {
           return callback(null, response);
        }
    });
}

var result = foo(function(err, result){
          if (!err)
           console.log(result);    
}); 
219
Hemant Bavle

Korkunç görünümlü, elle çizilmiş bir çizgi romanla cevap vereceğim. İkinci resim, kod örneğinizde result __undefined olmasının nedenidir.

 enter image description here

195

Angular1

AngularJS kullanan insanlar için, bu durumu Promises kullanarak yapabilir.

İşte diyor

Sözler eşzamanlı olmayan eşzamansız işlevlerde kullanılabilir ve birinin birden çok işlevi birlikte zincirlemesine izin verir.

Güzel bir açıklama burada da bulabilirsiniz.

docs içinde aşağıda verilen örnek.

  promiseB = promiseA.then(
    function onSuccess(result) {
      return result + 1;
    }
    ,function onError(err) {
      //Handle error
    }
  );

 // promiseB will be resolved immediately after promiseA is resolved 
 // and its value will be the result of promiseA incremented by 1.

Açısal2 ve Sonra

Angular2 içinde, aşağıdaki örneğe bakın, ancak recommended ile Angular2 ile Observables kullanın.

 search(term: string) {
     return this.http
  .get(`https://api.spotify.com/v1/search?q=${term}&type=artist`)
  .map((response) => response.json())
  .toPromise();

}

Bunu bu şekilde tüketebilirsiniz,

search() {
    this.searchService.search(this.searchField.value)
      .then((result) => {
    this.result = result.artists.items;
  })
  .catch((error) => console.error(error));
}

Buradaki original yazısına bakınız. Ancak TypeScript native es6 Promises özelliğini desteklemiyorsa, kullanmak istiyorsanız, bunun için bir eklentiye ihtiyacınız olabilir.

Ayrıca burada sözleri spec burada tanımlayın.

143
Maleen Abewardana

Buradaki yanıtların çoğu, tek bir eşzamansız işleminiz olduğunda faydalı önerilerde bulunur, ancak bazen bu, bir dizideki veya diğer liste benzeri bir yapıdaki her giriş için eşzamansız işlem yapmanız gerektiğinde ortaya çıkar. Temptation bunu yapmaktır:

// WRONG
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.Push(result);
    });
});
console.log(results); // E.g., using them, returning them, etc.

Örnek:

// WRONG
var theArray = [1, 2, 3];
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.Push(result);
    });
});
console.log("Results:", results); // E.g., using them, returning them, etc.

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

İşe yaramazlığın nedeni, sonuçları kullanmaya çalıştığınız zaman doSomethingAsync gelen geri aramaların henüz çalışmadığıdır.

Bu nedenle, bir diziniz varsa (veya bir tür listeniz varsa) ve her giriş için eşzamansız işlemler yapmak istiyorsanız, iki seçeneğiniz vardır: İşlemleri paralel (üst üste binen) veya seri olarak (sırayla arka arkaya) yapın.

Paralel

Hepsini başlatabilir ve kaç tane geri arama beklediğinizi takip edebilir ve ardından o kadar geri arama aldığınızda sonuçları kullanabilirsiniz:

var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

Örnek:

var theArray = [1, 2, 3];
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(expecting işlevini yerine getirebiliriz ve sadece results.length === theArray.length işlevini kullanabiliriz, ancak bu çağrılar beklenmedik durumdayken theArray öğesinin değişme olasılığını açığa çıkarır ...)

Sonuçların sıra dışı kalsa bile (async çağrıları başlatıldıkları sırayla tamamlanmadığından, sonucu index olarak forEach olarak results olarak kullandığımıza dikkat edin) ).

Peki ya return bu fonksiyondan bir sonuç çıkarsa ne olur? Diğer cevapların işaret ettiği gibi; fonksiyonunuzun bir geri aramayı kabul etmesi ve çağırması gerekir (veya bir Söz ver ). İşte bir geri arama sürümü:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

Örnek:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

Veya bunun yerine Promise işlevini döndüren bir sürüm:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Elbette, doSomethingAsync bize hatalar iletirse, bir hata olduğunda söz vermeyi reddetmek için reject kullanırdık.)

Örnek:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(Alternatif olarak, söz veren doSomethingAsync için bir sarmalayıcı yapabilir ve ardından aşağıdakileri yapabilirsiniz ...)

doSomethingAsync size bir Promise verirse, Promise.all :

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry);
    }));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

doSomethingAsync öğesinin ikinci ve üçüncü bir argümanı yok satacağını biliyorsanız, bunu doğrudan map öğesine iletebilirsiniz (map geri çağrısını üç argümanla çağırır, ancak çoğu kişi yalnızca ilkini kullanır):

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Örnek:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(doSomethingAsync));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

Promise.all 'un sözünü, hepsi çözümlendiğinde verdiğiniz sözlerin sonuçlarının bir dizisi ile çözdüğünü veya verdiğiniz sözlerin first sözlerini reddettiği zaman sözünü reddettiğini unutmayın.

Dizi

İşlemlerin paralel olmasını istemediğinizi varsayalım. Birbiri ardına çalıştırmak istiyorsanız, bir sonraki işleme başlamadan önce her bir işlemin tamamlanmasını beklemeniz gerekir. İşte bunu yapan ve sonuçla geri arama yapan bir fonksiyonun örneği:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.Push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

(Seri çalışmayı yaptığımızdan, sadece results.Push(result) işlevini kullanabiliriz, çünkü sıra dışı sonuç alamayacağımızı biliyoruz. Yukarıdakilerde results[index] = result; kullanabilirdik, ancak aşağıdaki örneklerin bazılarında Kullanılacak bir dizin yok.)

Örnek:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.Push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(Veya, yine, size vaat eden doSomethingAsync için bir sarmalayıcı oluşturun ve aşağıdakileri yapın ...)

doSomethingAsync size bir Söz verirse, ES2017 + sözdizimini kullanabilirseniz (belki de Babel gibi bir aktarıcıyla), for-of ve _ ile bir async işlevi kullanabilirsiniz. await :

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.Push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Örnek:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.Push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

ES2017 + sözdizimini kullanamıyorsanız (henüz), "Söz azaltma" deseni üzerinde bir varyasyon kullanabilirsiniz (bu, normal Söz Sözünün azaltmasından daha karmaşıktır çünkü sonucu bir satırdan geçmiyoruz) sonraki içine, ancak bunun yerine bir dizide sonuçlarını toplamak):

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.Push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

Örnek:

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.Push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

... ES2015 + ok işlevleri ile daha az hantaldır:

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.Push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

Örnek:

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.Push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}
122
T.J. Crowder

Şu örneğe bir göz atın:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope,$http) {

    var getJoke = function(){
        return $http.get('http://api.icndb.com/jokes/random').then(function(res){
            return res.data.value;  
        });
    }

    getJoke().then(function(res) {
        console.log(res.joke);
    });
});

Gördüğünüz gibi getJoke: a resolved promise (res.data.value döndürürken çözülür). Böylece, $ http.get isteği tamamlanana ve console.log (res.joke) çalıştırılıncaya kadar bekleyin (normal bir asenkron akış olarak).

Bu plnkr:

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/

ES6 yolu (zaman uyumsuz - bekliyor)

(function(){
  async function getJoke(){
    let response = await fetch('http://api.icndb.com/jokes/random');
    let data = await response.json();
    return data.value;
  }

  getJoke().then((joke) => {
    console.log(joke);
  });
})();
97

Eşzamansız bir işlevden değer döndürmek için başka bir yaklaşım, eşzamansız işlevden sonucu depolayacak bir nesneye geçmektir.

İşte aynı bir örnek:

var async = require("async");

// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.Push(function(_callback){
    // some asynchronous operation
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;
            _callback();
        }
    });
});

async.parallel(asyncTasks, function(){
    // result is available after performing asynchronous operation
    console.log(result)
    console.log('Done');
});

Zaman uyumsuz işlem sırasında değeri depolamak için result nesnesini kullanıyorum. Bu, sonucun zamanuyumsuz işten sonra bile kullanılabilir olmasını sağlar.

Bu yaklaşımı çok kullanırım. Sonucun ardışık modüller aracılığıyla kablolanmasının söz konusu olduğu yerlerde bu yaklaşımın ne kadar iyi çalıştığını bilmek isterdim.

85
jsbisht

Bu, iki farklı veri bağlama yönteminin birçok yeni JavaScript çerçevesinde kullanılan yerlerin sizin için harika çalışacağı yerlerden biri ...

Yani, Angular, React veya iki şekilde veri bağlama işlemini yapan başka çerçeveler kullanıyorsanız, bu sorun sizin için basitçe çözülür, bu nedenle kolay Word'de, sonuç şu şekilde undefinedname__: ilk aşamada, verileri almadan önce result = undefined aldınız, ardından sonucu alır almaz güncellenecek ve Ajax aramanızın yanıtı olan yeni değere atanacak ...

Peki saf javascript veya jQuery içinde bu soruda sorduğunuz gibi nasıl yapabilirsiniz?

Bir geri arama , söz ve son zamanlarda gözlemlenebilir kullanabilirsiniz, örneğin başarımız () veya sonra () gibi bazı fonksiyonlarımız var. verileriniz sizin için hazır olduğunda, geri arama veya abone ol işlevi açık gözlemlenebilir .

Örneğin, jQuery kullandığınız durumda, şöyle bir şey yapabilirsiniz:

$(document).ready(function(){
    function foo() {
        $.ajax({url: "api/data", success: function(data){
            fooDone(data); //after we have data, we pass it to fooDone
        }});
    };

    function fooDone(data) {
        console.log(data); //fooDone has the data and console.log it
    };

    foo(); //call happens here
});

Daha fazla bilgi için vaatler ve gözlemlenebilirler ki bu eşzamanlı olmayan maddeleri yapmanın yeni yolları.

80
Alireza

Sözler ve geri aramalar birçok durumda iyi çalışıyor olsa da, arkada şöyle bir şey ifade etmek acı vericidir:

if (!name) {
  name = async1();
}
async2(name);

async1 üzerinden geçeceksin; name undefined olup olmadığını kontrol edin ve geri çağrıyı buna göre arayın.

async1(name, callback) {
  if (name)
    callback(name)
  else {
    doSomething(callback)
  }
}

async1(name, async2)

Her ne kadar okay küçük örneklerde buna benzer birçok durum ve hata yönetimi varsa sinir bozucu olur.

Fibers, sorunun çözümünde yardımcı olur.

var Fiber = require('fibers')

function async1(container) {
  var current = Fiber.current
  var result
  doSomething(function(name) {
    result = name
    fiber.run()
  })
  Fiber.yield()
  return result
}

Fiber(function() {
  var name
  if (!name) {
    name = async1()
  }
  async2(name)
  // Make any number of async calls from here
}

Projeyi kontrol edebilirsiniz burada .

77
rohithpr

Kısa cevap, şöyle bir geri arama yapmanız gerekiyor:

function callback(response) {
    // Here you can do what ever you want with the response object.
    console.log(response);
}

$.ajax({
    url: "...",
    success: callback
});
73

Yazdığım aşağıdaki örnek nasıl yapılacağını gösteriyor

  • Eşzamansız HTTP çağrılarını yönetin;
  • Her bir API çağrısından yanıt bekleyin;
  • Kullanım Söz desen;
  • Birden fazla HTTP çağrısına katılmak için Promise.all pattern kullanın;

Bu çalışma örneği kendi kendine yetendir. Çağrıları yapmak için XMLHttpRequest nesnesini kullanan basit bir istek nesnesi tanımlayacaktır. Bir sürü sözün tamamlanmasını beklemek için basit bir işlev tanımlayacaktır.

Bağlam. Örnek, verilen bir sorgu dizeleri için playlist nesnelerini aramak amacıyla Spotify Web API endpoint'i sorguluyor:

[
 "search?type=playlist&q=%22Doom%20metal%22",
 "search?type=playlist&q=Adele"
]

Her bir öğe için, yeni bir Promise bir bloğu ateşleyecek - ExecutionBlock, sonucu ayrıştıracak, sonuç dizisini temel alan yeni bir dizi vaadi planlayacak, yani Spotify user nesnelerinin bir listesi olacak ve yeni HTTP çağrısını ExecutionProfileBlock içinde eşzamansız olarak yürütecektir.

Ardından, birden çok ve tamamen eşzamansız iç içe HTTP çağrıları oluşturmanıza olanak veren iç içe geçmiş bir Promise yapısını görebilir ve çağrıların her alt kümesindeki sonuçları Promise.all yoluyla birleştirebilirsiniz.

NOTERecent Spotify search API'leri, istek başlıklarında belirtilecek bir erişim belirteci gerektirecektir:

-H "Authorization: Bearer {your access token}" 

Bu nedenle, aşağıdaki örneği çalıştırmanız için erişim belirtecinizi istek başlıklarına koymanız gerekir:

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "<br/>"
    }
}

// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
        var request;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // Internet Explorer
            try {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                  request = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (e) {}
            }
        }

        // State changes
        request.onreadystatechange = function() {
            if (request.readyState === 4) { // Done
                if (request.status === 200) { // Complete
                    response(request.responseText)
                }
                else
                    response();
            }
        }
        request.open('GET', what, true);
        request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
        request.send(null);
    }
}

//PromiseAll
var promiseAll = function(items, block, done, fail) {
    var self = this;
    var promises = [],
                   index = 0;
    items.forEach(function(item) {
        promises.Push(function(item, i) {
            return new Promise(function(resolve, reject) {
                if (block) {
                    block.apply(this, [item, index, resolve, reject]);
                }
            });
        }(item, ++index))
    });
    Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
    }, function ErrorHandler(error) {
        if (fail) fail(error);
    });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
    var url = "https://api.spotify.com/v1/"
    url += item;
    console.log( url )
    SimpleRequest.call(url, function(result) {
        if (result) {

            var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
                return item.owner.href;
            })
            resolve(profileUrls);
        }
        else {
            reject(new Error("call error"));
        }
    })
}

arr = [
    "search?type=playlist&q=%22Doom%20metal%22",
    "search?type=playlist&q=Adele"
]

promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
        SimpleRequest.call(item, function(result) {
            if (result) {
                var obj = JSON.parse(result);
                resolve({
                    name: obj.display_name,
                    followers: obj.followers.total,
                    url: obj.href
                });
            } //result
        })
    } //ExecutionProfileBlock

    promiseAll(results[0], function(item, index, resolve, reject) {
        //console.log("Making request [" + index + "] " + item)
        ExecutionProfileBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
        console.log("All response received " + results.length);
        console.log(JSON.stringify(results, null, 2));
    }

    , function(error) { // Error
        console.log(error);
    })

    /////

  },
  function(error) { // Error
      console.log(error);
  });
<div id="console" />

Bu çözümü çok tartışmıştım here .

70
loretoparisi

2017 cevabı: şimdi her mevcut tarayıcıda ve düğümde tam olarak ne istersen yapabilirsin

Bu oldukça basit:

  • Bir söz ver
  • 'await' komutunu kullanın; bu, JavaScript’in bir değere çözümleneceği sözünü beklemesini söyler (HTTP yanıtı gibi).
  • Üst işlevine 'async' anahtar sözcüğünü ekleyin

İşte kodunuzun çalışan bir sürümü:

(async function(){

var response = await superagent.get('...')
console.log(response)

})()

bekliyor tüm mevcut tarayıcılarda ve düğüm 8'de destekleniyor

67
mikemaccana

Uzaktan arama yapmak için bu özel kütüphaneyi (Promise kullanılarak yazılmış) kullanabilirsiniz.

function $http(apiConfig) {
    return new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open(apiConfig.method, apiConfig.url);
        client.send();
        client.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                // Performs the function "resolve" when this.status is equal to 2xx.
                // Your logic here.
                resolve(this.response);
            }
            else {
                // Performs the function "reject" when this.status is different than 2xx.
                reject(this.statusText);
            }
        };
        client.onerror = function () {
            reject(this.statusText);
        };
    });
}

Basit kullanım örneği:

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});
60
Vinoth Rajendran

Js tek dişlidir.

Tarayıcı üç bölüme ayrılabilir:

1) Olay Döngüsü

2) Web API'si

3) Etkinlik Kuyruğu

Olay Döngüsü sonsuza kadar sürüyor, yani sonsuz türden bir döngü var.Event Queue, tüm işlevlerinizin bir olaya itildiği (örnek: klik) bu, sıradan yürütülen ve bu işlevi yürüten ve kendini hazırlayan Olay döngüsüne yerleştirilen teker tekerdir. ilki birinciden sonra bir tane yürütülür. Bu, bir işlevin yürütülmesi, sıradaki olay döngüsünde yürütülene kadar işlevin başlatılamayacağı anlamına gelir.

Şimdi sıradaki iki işlevi ittiğimizi düşünelim, biri sunucudan veri almak için diğeri bu verileri kullanır. Biz sıradaki serverRequest () işlevini önce iterek sonra da useiseData () işlevini ittik. serverRequest işlevi olay döngüsüne gider ve sunucudan veri almak için ne kadar zaman alacağımızı asla bilmediğimizden sunucuya çağrı yapar, bu nedenle bu işlemin zaman alması beklenir ve böylece olay döngüsümüzü meşgul ederiz, bu nedenle sayfamıza asılır; API, bu işlevi olay döngüsünden alır ve sunucuyu olay döngüsünü serbest hale getirir, böylece sıradaki işlevden sonraki işlevi çalıştırabiliriz. Sıradaki işlev, döngü içine giren ancak uygun veri olmadığı için döner. israf ve sonraki fonksiyonun yürütülmesi kuyruğun sonuna kadar devam eder. (Buna Async çağrısı denir, yani veri alana kadar başka bir şey yapabiliriz)

ServerRequest () işlevimizin bir kodda bir return ifadesi olduğunu varsayalım, sunucudan veri aldığımızda Web API'sini kuyruk sonunda kuyruğa sokar. Kuyruğun sonuna itildiğinden, bu verileri kullanmak için kuyruğumuzda kalan bir işlev olmadığından verilerini kullanamayız .Böylece Async Çağrısından bir şey döndürmek mümkün değildir.

Böylece bunun çözümü geri arama veya söz.

Buradaki yanıtlardan birinden bir görüntü, Geri arama kullanımını doğru şekilde açıklar ... İşlev çağrısı sunucusuna işlev (sunucudan döndürülen verileri kullanan işlev) işlevini veririz.

 CallBack

 function doAjax(callbackFunc, method, url) {
  var xmlHttpReq = new XMLHttpRequest();
  xmlHttpReq.open(method, url);
  xmlHttpReq.onreadystatechange = function() {

      if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
        callbackFunc(xmlHttpReq.responseText);
      }


  }
  xmlHttpReq.send(null);

}

Kodumda buna denir.

function loadMyJson(categoryValue){
  if(categoryValue==="veg")
  doAjax(print,"GET","http://localhost:3004/vegetables");
  else if(categoryValue==="fruits")
  doAjax(print,"GET","http://localhost:3004/fruits");
  else 
  console.log("Data not found");
}

Zaman uyumsuz arama yapmak için ECMA'daki (2016/17) yeni yöntemler için burayı okuyun (@Felix Kling'in Tepesinde Cevapla) https://stackoverflow.com/a/14220323/7579856

55
Aniket Jha

Diğer bir çözüm de, kod sıralı yürütücü nsynjs aracılığıyla yürütmektir.

Altta yatan fonksiyon söz veriliyorsa

nsynjs tüm sözleri ardışık olarak değerlendirecek ve vaat sonucunu data özelliğine ekleyecektir:

function synchronousCode() {

    var getURL = function(url) {
        return window.fetch(url).data.text().data;
    };
    
    var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
    console.log('received bytes:',getURL(url).length);
    
};

nsynjs.run(synchronousCode,{},function(){
    console.log('synchronousCode done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

Temel fonksiyon söz verilmezse

Adım 1. Geri çağırma işlevini nsynjs-farkında sarmalayıcıya sarın (söz verilmiş sürümü varsa, bu adımı atlayabilirsiniz):

var ajaxGet = function (ctx,url) {
    var res = {};
    var ex;
    $.ajax(url)
    .done(function (data) {
        res.data = data;
    })
    .fail(function(e) {
        ex = e;
    })
    .always(function() {
        ctx.resume(ex);
    });
    return res;
};
ajaxGet.nsynjsHasCallback = true;

Adım 2. Fonksiyona senkron mantığı yerleştirin:

function process() {
    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}

Adım 3. nsynjs ile fonksiyonu senkronize bir şekilde çalıştırın:

nsynjs.run(process,this,function () {
    console.log("synchronous function finished");
});

Nsynjs, bazı yavaş fonksiyonların sonuçlarının hazır olmaması durumunda yürütmeyi duraklatan tüm operatörleri ve ifadeleri adım adım değerlendirecek.

Burada daha fazla örnek: https://github.com/amaksr/nsynjs/tree/master/examples

54
amaksr

JavaScript'in gizemleri ile mücadele ederken karşılaştığımız çok yaygın bir konudur. Bugün bu gizemi açığa çıkarmayı deneyeyim.

Basit bir JavaScript işlevi ile başlayalım:

function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

Bu basit bir senkronize fonksiyon çağrısı (her kod satırının bir sonraki sırada olandan önce 'işi ile bittiği') ve sonuç beklendiği gibi aynı.

Şimdi, fonksiyonumuza küçük bir gecikme ekleyerek biraz kod ekleyelim, böylece tüm kod satırları sırayla 'bitmedi'. Böylece, asenkron işlevin davranışını taklit eder:

function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

Öyleyse işte, bu gecikme beklediğimiz işlevselliği bozdu! Ama tam olarak ne oldu? Eh, koda bakarsanız oldukça mantıklı. foo() işlevi, yürütme sırasında hiçbir şey döndürmez (bu nedenle döndürülen değer undefinedname__'dir), ancak 'wohoo' işlevini döndürmek için 1s sonra bir işlevi yürüten bir zamanlayıcı başlatır. Ama gördüğünüz gibi, çubuğa atanan değer, daha sonra gelen başka bir şey değil, foo () 'dan derhal geri gönderilen şeylerdir.

Peki, bu konuyla nasıl başa çıkacağız?

Fonksiyonumuzu aPROMISEiçin soralım. Söz vermek gerçekten ne anlama geldiğidir: bu, işlevin gelecekte elde edeceği herhangi bir çıktıyı sağlamanızı garanti ettiği anlamına gelir. öyleyse, yukarıdaki küçük sorunumuz için eylemde görelim:

function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when execution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});

Bu nedenle, özet - ajax tabanlı çağrılar vb. Gibi asenkron işlevlerin üstesinden gelmek için, resolvedeğerine (geri dönmek istediğiniz) bir söz verebilirsiniz. Böylece, kısaca asenkronize fonksiyonlarda return yerine/değerini kullanırsınız.

GÜNCELLEME (Async/bekliyor ile vaat ediyor)

Vaatlerle çalışmak için then/catch kullanmanın yanı sıra, bir yaklaşım daha var. Fikir, bir sonraki kod satırına geçmeden önce zaman uyumsuz bir işlevi tanıma ve sonra vaatleri bekle 'in çözülmesidir. Hala başlık altında sadece promisesname__, ancak farklı bir sözdizimsel yaklaşım ile. İşleri netleştirmek için, aşağıda bir karşılaştırma bulabilirsiniz:

sonra/sürüm yakalamak:

function fetchUsers(){
   let users = [];
   getUsers()
   .then(_users => users = _users)
   .catch(err =>{
      throw err
   })
   return users;
 }

async/bekliyor sürümü:

  async function fetchUsers(){
     try{
        let users = await getUsers()
        return users;
     }
     catch(err){
        throw err;
     }
  }
42
Anish K.

ECMAScript 6, asenkron bir tarzda kolayca programlamanıza izin veren 'jeneratörlere' sahiptir.

function* myGenerator() {
    const callback = yield;
    let [response] = yield $.ajax("https://stackoverflow.com", {complete: callback});
    console.log("response is:", response);

    // examples of other things you can do
    yield setTimeout(callback, 1000);
    console.log("it delayed for 1000ms");
    while (response.statusText === "error") {
        [response] = yield* anotherGenerator();
    }
}

Yukarıdaki kodu çalıştırmak için şunu yapın:

const gen = myGenerator(); // Create generator
gen.next(); // Start it
gen.next((...args) => gen.next([...args])); // Set its callback function

ES6'yı desteklemeyen tarayıcıları hedeflemeniz gerekirse, ECMAScript 5'i oluşturmak için kodu Babel veya closure compiler aracılığıyla çalıştırabilirsiniz.

Geri arama ...args bir diziye sarılır ve bunları okuduğunuzda imha edilir, böylece desen birden fazla argüman içeren geri aramalarla baş edebilir. Örneğin, düğümü fs ile:

const [err, data] = yield fs.readFile(filePath, "utf-8", callback);
36
James

Asenkron isteklerle çalışmak için bazı yaklaşımlar:

  1. Tarayıcı Söz nesnesi
  2. Q - JavaScript için söz kütüphanesi
  3. A + Promises.js
  4. ertelenen jQuery
  5. XMLHttpRequest API
  6. Geri arama kavramının kullanılması - İlk cevapta uygulama olarak

Örnek: jQuery birden çok istekle çalışmak için ertelenen uygulama

var App = App || {};

App = {
    getDataFromServer: function(){

      var self = this,
                 deferred = $.Deferred(),
                 requests = [];

      requests.Push($.getJSON('request/ajax/url/1'));
      requests.Push($.getJSON('request/ajax/url/2'));

      $.when.apply(jQuery, requests).done(function(xhrResponse) {
        return deferred.resolve(xhrResponse.result);
      });
      return deferred;
    },

    init: function(){

        this.getDataFromServer().done(_.bind(function(resp1, resp2) {

           // Do the operations which you wanted to do when you
           // get a response from Ajax, for example, log response.
        }, this));
    }
};
App.init();
36
Mohan Dere

Kısa cevap : foo() yönteminiz derhal geri döner, $ajax() çağrısı eşzamansız olarak yürütülür işlev döndükten sonra . Asıl sorun, zaman uyumsuz çağrı tarafından alınan sonuçların geri döndüğünde nasıl veya nereye kaydedileceğidir.

Bu iş parçacığında çeşitli çözümler verilmiştir. Belki de en kolay yol, bir nesneyi foo() yöntemine iletmek ve sonuçları eşzamansız çağrı tamamlandıktan sonra bu nesnenin bir üyesinde saklamaktır.

function foo(result) {
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;   // Store the async result
        }
    });
}

var result = { response: null };   // Object to hold the async result
foo(result);                       // Returns before the async completes

foo() işlevine yapılan çağrının işe yarar bir şey döndürmeyeceğini unutmayın. Ancak, zaman uyumsuz arama sonucu artık result.response içinde saklanır.

33
David R Tribble

callback() başarısında bir foo() işlevini kullanın. Bu şekilde dene. Basit ve anlaşılması kolaydır.

var lat = "";
var lon = "";
function callback(data) {
    lat = data.lat;
    lon = data.lon;
}
function getLoc() {
    var url = "http://ip-api.com/json"
    $.getJSON(url, function(data) {
        callback(data);
    });
}

getLoc();
33
Mahfuzur Rahman

Tabii ki senkron istek, vaadi gibi birçok yaklaşım var, ama deneyimlerime göre geri arama yaklaşımını kullanmalısın. Javascript’in eşzamansız davranışı doğaldır. Böylece, kod snippet'iniz biraz farklı olabilir:

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}
26
Khoa Bui

Soru şuydu:

Zaman uyumsuz bir çağrının yanıtını nasıl döndürürüm?

hangi olarak yorumlanabilir:

Eşzamansız kodunun eşzamanlı ifadesi nasıl görünür?

Çözüm, geri aramalardan kaçınmak ve Promises ve async/await kombinasyonunu kullanmak olacaktır.

Bir Ajax talebi için bir örnek vermek istiyorum.

(Javascript'te yazılabilmesine rağmen, Python'da yazmayı ve Transcrypt kullanarak Javascript'te derlemeyi tercih ediyorum.)

Önce JQuery kullanımını etkinleştirelim, $Sname__:

__pragma__ ('alias', 'S', '$')

Promise döndüren bir işlev tanımlayın, bu durumda bir Ajax çağrısı:

def read(url: str):
    deferred = S.Deferred()
    S.ajax({'type': "POST", 'url': url, 'data': { },
        'success': lambda d: deferred.resolve(d),
        'error': lambda e: deferred.reject(e)
    })
    return deferred.promise()

Asenkron kodunu, sanki senkron 'u kullanın:

async def readALot():
    try:
        result1 = await read("url_1")
        result2 = await read("url_2")
    except Exception:
        console.warn("Reading a lot failed")
24

ES2017'yi kullanarak bunu işlev bildirimi olarak almalısınız.

async function foo() {
    var response = await $.ajax({url: '...'})
    return response;
}

Ve bu şekilde yürütmek.

(async function() {
    try {
        var result = await foo()
        console.log(result)
    } catch (e) {}
})()

Veya söz sözdizimi

foo().then(response => {
    console.log(response)

}).catch(error => {
    console.log(error)

})
14

Size kod yazmak yerine, JS'nin geri aramaları ve eşzamansızlığı nasıl ele aldığını anlamak için anahtar olan 2 kavram vardır. (bu bir kelime bile mi?)

Olay Döngüsü ve Eşzamanlılık Modeli

Dikkat etmeniz gereken üç şey var; Sıra; olay döngüsü ve yığın

Geniş, basit terimlerle, olay döngüsü proje yöneticisi gibidir, sıra ile yığın arasında çalıştırmak ve iletişim kurmak isteyen tüm fonksiyonları sürekli olarak dinler.

while (queue.waitForMessage()) {
   queue.processNextMessage();
}

Bir şeyi çalıştırmak için bir mesaj aldığında, onu sıraya ekler. Sıra, yürütmeyi bekleyenlerin listesidir (AJAX isteğiniz gibi). böyle düşünün:

 1. call foo.com/api/bar using foobarFunc
 2. Go perform an infinite loop
 ... and so on

Bu mesajlardan biri çalıştırılacaksa, mesaj kuyruğundan çıkar ve bir yığın oluşturur, yığın mesajın talimatını yerine getirmek için JS'nin yürütmesi gereken her şeydir. Yani bizim örneğimizde foobarFuncçağrısı yapılıyor.

function foobarFunc (var) {
  console.log(anotherFunction(var));
}

Bu nedenle foobarFunc uygulamasının çalışması gereken her şey (bizim durumumuzda anotherFunctionname__) yığına itilir. yürütüldü ve sonra unutuldu - olay döngüsü daha sonra sıradaki bir sonraki şeye geçecektir (veya mesajları dinler)

Buradaki en önemli şey yürütme sırasıdır. Bu

WHEN kaçacak bir şey

Harici bir partiye AJAX kullanarak bir arama yaptığınızda veya herhangi bir zaman uyumsuz kodu (örneğin bir setTimeout) çalıştırdığınızda, Javascript devam etmeden önce bir cevaba bağlıdır.

Büyük soru ne zaman yanıt alacağı? Cevap bilmiyoruz - bu yüzden olay döngüsü bu mesajın "hey koş beni" demesini bekliyor. Eğer JS bu mesaj için eşzamanlı olarak bekleseydi, uygulamanız donar ve emer. Böylece JS, sıradaki iletinin sıraya eklenmesini beklerken sıradaki bir sonraki öğeyi çalıştırmaya devam eder.

Bu yüzden asenkron işlevsellikte callbacks denilen şeyleri kullanıyoruz. Tam anlamıyla bir söz gibi. Bir noktada bir şeyi geri getireceğime dair söz verdiğimde jQuery, deffered.donedeffered.fail ve deffered.always (diğerleri arasında) adı verilen belirli geri aramaları kullanır. Hepsini görebilirsiniz burada

Yani yapmanız gereken şey, kendisine iletilen verilerle bir noktada yürütmeye söz verilen bir işlevi geçmek.

Bir geri çağırma hemen yürütülmediğinden, ancak daha sonraki bir zamanda, başvuruyu yürütülmeyen işleve iletmek önemlidir. yani

function foo(bla) {
  console.log(bla)
}

bu yüzden çoğu zaman (ancak her zaman değil) geçeceksiniz foodeğil foo()

Umarım bu biraz mantıklı olur. Böyle kafa karıştırıcı görünen bir şeyle karşılaştığınızda - en azından anlamanız için belgeleri tamamen okumanızı tavsiye ederim. Seni çok daha iyi bir geliştirici yapacaktır.

13
Matthew Brent

Ağaçlara bakmadan önce ormanı görelim.

Burada ayrıntılı bilgi içeren birçok bilgilendirici cevap var, hiçbirini tekrar etmeyeceğim. JavaScript'te programlamanın anahtarı ilk önce genel yürütmenin doğru zihinsel modeline sahip olmaktır.

  1. Giriş noktalarınız bir etkinliğin sonucu olarak yürütülür. Örneğin, tarayıcıya kod içeren bir komut dosyası etiketi yüklenmiştir. (Buna göre, bu nedenle öncelikle dom öğeleri oluşturulması gerekiyorsa, kodunuzu çalıştırmaya sayfanın hazır olmasıyla ilgilenmeniz gerekebilir.)
  2. Kodunuz tamamlanması için yürütür - ancak XHR istekleri, ayar zaman aşımları, dom olay işleyicileri, vb. Dahil olmak üzere geri aramalarınızın herhangi birini gerçekleştirmeden birçok eşzamanlı olmayan çağrı yapar. Bir sıra, işten çıkarılan diğer olayların tamamının idamını takiben yürütülmesini bekleyen.
  3. Bir XHR isteğine yapılan her bir geri arama, bir kez çağrılan olayın zaman aşımını veya dom'u ayarladıktan sonra tamamlanmaya devam eder.

İyi haber şu ki, eğer bu noktayı iyi anlarsanız, yarış koşulları konusunda asla endişelenmenize gerek kalmayacak. Öncelikle, en başta farklı ayrık olaylara cevap olarak kodunuzu nasıl düzenlemek istediğinize ve bunları nasıl bir mantıksal sıraya dizmek istediğinize ilişkin bir şey yapmalısınız. Bu amaç için araçlar olarak vaatler veya daha üst düzey yeni eşzamansız/beklemenizi kullanabilir ya da kendiniz için alabilirsiniz.

Ancak bir problemi çözmek için asıl problem alanı ile rahat olana kadar taktiksel bir araç kullanmamalısınız. Ne zaman çalışması gerektiğini bilmek için bu bağımlılıkların bir haritasını çizin. Tüm bu geri aramalara geçici bir yaklaşım denemek sadece size iyi hizmet etmeyecektir.

13
Haim Zamir

Söz Kullanımı

Bu sorunun en mükemmel cevabı Promise kullanıyor.

function ajax(method, url, params) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open(method, url);
    xhr.send(params);
  });
}

Kullanım

ajax("GET", "/test", "acrive=1").then(function(result) {
    // Code depending on result
})
.catch(function() {
    // An error occurred
});

Fakat bekle...!

Vaatleri kullanmada bir sorun var!

Neden kendi özel Sözümüzü kullanmalıyız?

Eski tarayıcılarda bir hata olduğunu bulana kadar bir süre bu çözümü kullanıyordum:

Uncaught ReferenceError: Promise is not defined

Bu yüzden ES3'ten aşağıya js derleyicileri için tanımlanmamışsa kendi Söz Sınıfımı uygulamaya karar verdim. Sadece bu kodu ana kodunuzdan önce ekleyin ve ardından güvenlik kullanıcısı Promise!

if(typeof Promise === "undefined"){
    function _classCallCheck(instance, Constructor) {
        if (!(instance instanceof Constructor)) { 
            throw new TypeError("Cannot call a class as a function"); 
        }
    }
    var Promise = function () {
        function Promise(main) {
            var _this = this;
            _classCallCheck(this, Promise);
            this.value = undefined;
            this.callbacks = [];
            var resolve = function resolve(resolveValue) {
                _this.value = resolveValue;
                _this.triggerCallbacks();
            };
            var reject = function reject(rejectValue) {
                _this.value = rejectValue;
                _this.triggerCallbacks();
            };
            main(resolve, reject);
        }
        Promise.prototype.then = function then(cb) {
            var _this2 = this;
            var next = new Promise(function (resolve) {
                _this2.callbacks.Push(function (x) {
                    return resolve(cb(x));
                });
            });
            return next;
        };
        Promise.prototype.catch = function catch_(cb) {
            var _this2 = this;
            var next = new Promise(function (reject) {
                _this2.callbacks.Push(function (x) {
                    return reject(cb(x));
                });
            });
            return next;
        };
        Promise.prototype.triggerCallbacks = function triggerCallbacks() {
            var _this3 = this;
            this.callbacks.forEach(function (cb) {
                cb(_this3.value);
            });
        };
        return Promise;
    }();
}
12
Amir Forsati

İşte işe yarayan bir örnek:

const validateName = async userName => {
  const url = "abc/xyz";
  try {
    const response = await axios.get(url);
    return response.data
  } catch (err) {
    return false;
  }
};

validateName("user")
 .then(data => console.log(data))
 .catch(reason => console.log(reason.message))
7
Alex Montoya

İstek, eşzamanlı olmayan şekilde çalışır, böylece verileri normal kodla eşzamanlı şekilde okuyamazsınız. Ancak, async/await kullanarak, senkronizasyon koduna yakın/benzeyen bir uyumsuz kod oluşturabilirsiniz. İşlem isteği verilerinin kodunun async işlevi ile sarılması gerekiyor (_ [pasajın altında load tuşunun altında]) ve içine foo() öğesinden önce await keywort eklemeniz gerekir (ayrıca async/await kullanın ).

async function foo() {
  var url= 'https://jsonplaceholder.typicode.com/todos/1';
  var result= await (await fetch(url)).text(); // or .json()
  return result;
}

async function load() {
  var result = await foo();
  console.log(result);
}

load();
5

async/await'un daha eski tarayıcılarda çalışmasını sağlamak için Babel gibi bir aktarıcı ile kullanılması. Ayrıca bu Babil önayarını ve çoklu dolguyu npm: npm i - D babel-preset-env.

function getData(ajaxurl) { 
  return $.ajax({
    url: ajaxurl,
    type: 'GET',
  });
};

async test() {
  try {
    const res = await getData('https://api.icndb.com/jokes/random')
    console.log(res)
  } catch(err) {
    console.log(err);
  }
}

test();

veya .then geri çağırma, aynı mantığı yazmanın başka bir yoludur.

getData(ajaxurl).then(function(res) {
    console.log(res)
}
1
Murtaza Hussain