Eşzamansız bir aramadan gelen yanıtı nasıl döndürürüm?

Jan 09 2013

Ben bir işlevi var foouyumsuz bir istekte bulunur. Yanıtı / sonucu nasıl iade edebilirim foo?

Geri aramadan değeri döndürmeyi denedim, aynı zamanda sonucu işlevin içindeki yerel bir değişkene atamayı ve onu döndürmeyi denedim, ancak bu yollardan hiçbiri aslında yanıtı döndürmüyor (hepsi dönüyor undefinedveya değişkenin başlangıç ​​değeri ne olursa olsun result) .

JQuery ajaxişlevini kullanan örnek :

function foo() {
    var result;

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

    return result; // It always returns `undefined`
}

Node.js kullanma örneği:

function foo() {
    var result;

    fs.readFile("path/to/file", function(err, data) {
        result = data;
        // return data; // <- I tried that one as well
    });

    return result; // It always returns `undefined`
}

thenBir söz bloğunun kullanıldığı örnek :

function foo() {
    var result;

    fetch(url).then(function(response) {
        result = response;
        // return response; // <- I tried that one as well
    });

    return result; // It always returns `undefined`
}

Yanıtlar

5905 FelixKling Jan 09 2013 at 00:06

→ Farklı örneklerle zaman uyumsuz davranışın daha genel bir açıklaması için, lütfen değişkenim bir işlevin içinde değiştirdikten sonra neden değiştirilmedi? Konusuna bakın. - Eşzamansız kod referansı

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

Sorun

Bir de Ajax açılımı asenkron . Bu, isteğin gönderilmesi (veya yanıtı almanın) normal yürütme akışından çıkarıldığı anlamına gelir. Örneğinizde, $.ajaxhemen döner ve bir sonraki ifade, geri arama olarak return result;geçirdiğiniz işlev successçağrılmadan önce çalıştırılır .

Eşzamanlı ve eşzamansız akış arasındaki farkı daha net hale getirmesini umduğumuz bir benzetme:

Senkron

Bir arkadaşınızla bir telefon görüşmesi yaptığınızı ve ondan sizin için bir şeyler aramasını istediğinizi hayal edin. Biraz zaman alsa da, arkadaşınız size ihtiyacınız olan cevabı verene kadar telefonda beklersiniz ve boşluğa bakarsınız.

Aynı şey, "normal" kod içeren bir işlev çağrısı yaptığınızda da olur:

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

var item = findItem();

// Do something with item
doSomethingElse();

Yürütülmesi findItemuzun zaman alsa da, sonrasında gelen herhangi bir kod , işlevin sonucu döndürmesini beklemekvar item = findItem(); zorundadır .

Eşzamansız

Arkadaşını yine aynı sebepten arıyorsun. Ama bu sefer ona aceleniz olduğunu ve sizi cep telefonunuzdan araması gerektiğini söylüyorsunuz . Telefonu kapatırsın, evi terk edersin ve yapmayı planladığın her şeyi yaparsın. Arkadaşınız sizi geri aradığında, size verdiği bilgilerle ilgileniyorsunuz.

Ajax isteği yaptığınızda olan tam olarak budur.

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

Yanıtı beklemek yerine, yürütme hemen devam eder ve Ajax çağrısının ardından ifade çalıştırılır. Sonunda yanıtı almak için, yanıt alındığında çağrılacak bir işlev, bir geri arama (bir şey fark ettiniz mi? Geri aramak mı?) Sağlarsınız . Bu aramadan sonra gelen herhangi bir ifade, geri arama çağrılmadan önce yürütülür.


Çözüm (ler)

JavaScript'in eşzamansız doğasını kucaklayın! Bazı eşzamansız işlemler eşzamanlı emsaller sağlasa da ("Ajax" da öyle), bunları özellikle tarayıcı bağlamında kullanmak genellikle tavsiye edilmez.

Neden soruyorsun kötü?

JavaScript, tarayıcının UI iş parçacığında çalışır ve uzun süren herhangi bir işlem UI'yi kilitleyerek yanıt vermemesine neden olur. Ek olarak, JavaScript için yürütme süresinde bir üst sınır vardır ve tarayıcı, kullanıcıya yürütmeye devam edip etmeyeceğini sorar.

Bunların hepsi gerçekten kötü bir kullanıcı deneyimi. Kullanıcı her şeyin yolunda gidip gitmediğini anlayamayacak. Ayrıca, bağlantısı yavaş olan kullanıcılar için etki daha kötü olacaktır.

Aşağıda, hepsi birbirinin üzerine inşa edilen üç farklı çözüme bakacağız:

  • İle Vaatlerasync/await (bir transpiler veya rejeneratör kullanırsanız eski tarayıcılarda kullanılabilir ES2017 +,)
  • Geri aramalar (düğümde popüler)
  • then()(ES2015 +, vaat edilen birçok kitaplıktan birini kullanıyorsanız eski tarayıcılarda mevcuttur) ile vaatler

Üçü de mevcut tarayıcılarda ve 7+ düğümünde mevcuttur.


ES2017 +: Vaatler async/await

2017'de yayınlanan ECMAScript sürümü , eşzamansız işlevler için sözdizimi düzeyinde destek sunmuştur. Yardımıyla asyncve awaitbir "senkron tarzı" asenkron yazabilir. Kod hala eşzamansızdır, ancak okunması / anlaşılması daha kolaydır.

async/awaitvaatlerin üzerine inşa edilir: bir asyncişlev her zaman bir söz verir. awaitBir sözü "çözer" ve sözün çözüldüğü değerle sonuçlanır veya söz reddedilirse bir hata verir.

Önemli: Yalnızca awaitbir asyncişlevin içinde kullanabilirsiniz . Şu anda üst düzey awaithenüz desteklenmiyor, bu nedenle bir bağlam başlatmak için bir asenkron IIFE ( Anında Çağrılan İşlev İfadesi ) yapmanız gerekebilir async.

MDN hakkında asyncve awaitMDN hakkında daha fazla bilgi edinebilirsiniz .

İşte yukarıdaki gecikmenin üzerine inşa edilen 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);
})();

Mevcut tarayıcı ve düğüm sürümleri desteklenir async/await. Rejeneratör (ya da Babel gibi rejeneratör kullanan araçlar) yardımıyla kodunuzu ES5'e dönüştürerek eski ortamları da destekleyebilirsiniz .


İşlevlerin geri aramaları kabul etmesine izin verin

Geri arama, işlev 1'in işlev 2'ye geçmesidir. İşlev 2, hazır olduğunda işlev 1'i çağırabilir. Eşzamansız bir işlem bağlamında, eşzamansız işlem her yapıldığında geri arama çağrılacaktır. Genellikle sonuç geri aramaya aktarılır.

Soru örneğinde, foobir geri aramayı kabul edebilir ve onu successgeri arama olarak kullanabilirsiniz . Yani bu

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

olur

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

Burada "satır içi" işlevini tanımladık, ancak herhangi bir işlev başvurusunu iletebilirsiniz:

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

foo(myCallback);

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

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

callbackfooonu çağırdığımızda geçtiğimiz işleve atıfta bulunacak ve onu aktaracağız success. Yani Ajax isteği başarılı olduğunda $.ajax, arar callbackve yanıtı geri aramaya iletir (geri aramayı resultböyle tanımladığımız için buna başvurulabilir ).

Yanıtı geri aramaya iletmeden ö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. Sonuçta, tarayıcıdaki JavaScript büyük ölçüde olay odaklıdır (DOM olayları). Ajax yanıtını almak bir olaydan başka bir şey değildir.
Üçüncü taraf kodla çalışmanız gerektiğinde zorluklar ortaya çıkabilir, ancak çoğu sorun yalnızca uygulama akışını düşünerek çözülebilir.


ES2015 +: O zaman vaat ediyor ()

Promise API ECMAScript'e 6 (ES2015) yeni bir özelliktir, ama iyi vardır tarayıcı desteği zaten. Ayrıca standart Promises API'yi uygulayan ve asenkron işlevlerin (örn. Bluebird ) kullanımını ve bileşimini kolaylaştırmak için ek yöntemler sağlayan birçok kitaplık vardır .

Sözler, gelecekteki değerlerin kaplarıdır . Söz, değeri aldığında ( çözüldüğünde ) veya iptal edildiğinde ( reddedildiğinde ), bu değere erişmek isteyen tüm "dinleyicilerine" bildirimde bulunur.

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

İşte bir söz kullanma ö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, aşağıdaki gibi vaatleri kullanabilirdik:

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ı anlatmak bu cevabın kapsamı dışındadır, ancak yeni bir kod yazarsanız, bunları ciddiye almalısınız. Kodunuzun büyük bir soyutlamasını ve ayrılmasını sağlarlar.

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

Yan not: jQuery'nin ertelenmiş nesneleri

Ertelenen nesneler , jQuery'nin özel vaat uygulamasıdır (Promise API standartlaştırılmadan önce). Neredeyse vaatler gibi davranırlar, ancak biraz farklı bir API ortaya çıkarırlar.

JQuery'nin her Ajax yöntemi, işlevinizden 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

Vaatlerin ve ertelenen nesnelerin yalnızca gelecekteki bir değer için kaplar 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ızlık sorunlarını yanlış anlar. Özellikle, $.ajax()sunucunuzdaki '/ password' sayfasını kontrol ederken kodu dondurmaz - sunucuya bir istek gönderir ve beklerken, sunucudan gelen yanıtı değil, hemen bir jQuery Ajax Ertelenmiş nesnesi döndürür. Bu, ififadenin her zaman bu Ertelenmiş nesneyi alacağı, ona davranacağı trueve kullanıcı oturum açmış gibi devam edeceği anlamına gelir . İyi değil.

Ancak düzeltmek 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
});

Önerilmez: Eşzamanlı "Ajax" çağrıları

Bahsettiğim gibi, bazı (!) Eşzamansız işlemlerin eşzamanlı karşılıkları vardır. Kullanımlarını savunmuyorum, ancak bütünlük adına, işte eşzamanlı bir aramayı şu şekilde gerçekleştirirsiniz:

JQuery olmadan

Doğrudan bir XMLHttpRequestnesne kullanıyorsanız, falseüçüncü argüman olarak iletin .open.

jQuery

JQuery kullanıyorsanız , asyncseçeneği olarak ayarlayabilirsiniz false. Bu seçeneğin jQuery 1.8'den beri kullanımdan kaldırıldığını unutmayın . Daha sonra bir successgeri aramayı kullanmaya devam edebilir veya jqXHR nesnesininresponseText özelliğine erişebilirsiniz :

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

Başka jQuery Ajax yöntemi gibi kullanırsanız $.get, $.getJSONvb, aşağıda belirtilen yerlere değiştirmek zorunda $.ajax(sadece yapılandırma parametreleri geçirebilirsiniz beri $.ajax).

Önüne bak! Eşzamanlı bir JSONP isteği yapmak mümkün değildir . JSONP doğası gereği her zaman eşzamansızdır (bu seçeneği dikkate almamak için bir neden daha).

1096 BenjaminGruenbaum May 30 2013 at 06:30

Eğer ediyorsanız değil kodunuzda jQuery kullanarak, bu cevap size göre

Kodunuz şu satırlarda 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şiler için bir cevap yazarak iyi bir iş çıkardı, olmayan insanlar için bir alternatif sağlamaya karar verdim.

( Not, yeni fetchAPI, Angular veya sözler kullananlar için aşağıya başka bir cevap ekledim )


Neyle yüzleşiyorsun

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

Bir AJAX açılımı asenkron . Bu, isteğin gönderilmesi (veya yanıtı almanın) normal yürütme akışından çıkarıldığı anlamına gelir. Örneğinizde, .sendhemen döner ve bir sonraki ifade, geri arama olarak return result;geçirdiğiniz işlev successçağrılmadan önce çalıştırılır .

Bu, geri döndüğünüzde, tanımladığınız dinleyicinin henüz çalışmadığı anlamına gelir, bu, 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;
}

(Vaktini boşa harcamak)

aDöndürülen değer undefined, a=5parça henüz çalıştırılmadığından beri . AJAX bu şekilde davranır, sunucu tarayıcınıza bu değerin ne olduğunu söyleme şansı vermeden önce değeri döndürürsünüz.

Bu soruna olası bir çözüm , programınıza hesaplama tamamlandığında ne yapacağını söyleyerek yeniden kodlama yapmaktır.

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 denir . Temel olarak, getFivetamamlandığında gerçekleştirilecek bir eylemi iletiyoruz, kodumuza bir olay tamamlandığında nasıl tepki vereceğini söylüyoruz (AJAX çağrımız veya bu durumda zaman aşımı gibi).

Kullanım şöyle olacaktır:

getFive(onComplete);

Hangi ekrana "5" uyarısı vermelidir. (Keman) .

Muhtemel çözümler

Bunu çözmenin temelde iki yolu vardır:

  1. AJAX çağrısını eşzamanlı yapın (buna SJAX diyelim).
  2. Geri aramalarla düzgün çalışması için kodunuzu yeniden yapılandırın.

1. Eşzamanlı AJAX - Yapmayın !!

Senkronize AJAX'a gelince, bunu yapmayın! Felix'in cevabı, bunun neden kötü bir fikir olduğu konusunda bazı ikna edici argümanlar doğurur. Ö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 olduğuna dair başka bir kısa özet:

XMLHttpRequest hem eşzamanlı hem de eşzamansız iletişimleri destekler. Ancak genel olarak, performans nedenlerinden ötürü zaman uyumsuz istekler, eşzamanlı isteklere tercih edilmelidir.

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

Eğer varsa sahip bunu yapmak için, bir bayrak geçirebilirsiniz: Bu şekilde yapılabilir:

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. Kodu yeniden yapılandırın

Fonksiyonunuzun bir geri aramayı kabul etmesine izin verin. Örnekte, foobir geri aramayı kabul etmek için kod yapılabilir. Biz nasıl bizim kod söylüyorum olacak tepki zaman footamamlar.

Yani:

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

Olur:

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

Burada anonim bir işlevi geçtik, ancak mevcut bir işleve bir referansı da aynı şekilde kolayca iletebilir ve şöyle görünmesini sağlayabiliriz:

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ına bakın.

Şimdi, buna göre hareket etmek için foo'nun kendisini 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();
}

(Vaktini boşa harcamak)

Şimdi foo fonksiyonumuzun AJAX başarıyla tamamlandığında çalışacak bir eylemi kabul etmesini sağladık, yanıt durumunun 200 olup olmadığını kontrol ederek ve buna göre hareket ederek (bir hata işleyicisi vb. Oluşturun) bunu daha da genişletebiliriz. Sorunumuzu etkili bir şekilde çözüyoruz.

Hala bunu anlamakta güçlük çekiyorsanız , MDN'deki AJAX başlangıç ​​kılavuzunu okuyun .

411 cocco Aug 19 2013 at 15:06

XMLHttpRequest 2 (öncelikle Benjamin Gruenbaum ve Felix Kling'in yanıtlarını okuyun)

JQuery kullanmıyorsanız ve modern tarayıcılarda ve ayrıca mobil tarayıcılarda çalışan güzel ve kısa bir XMLHttpRequest 2 istiyorsanız, şu ş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ısadır.
  2. Geri arama doğrudan ayarlanır (bu nedenle fazladan gereksiz kapatmalar olmaz).
  3. Yeni onload'u kullanır (böylece readystate && durumunu kontrol etmeniz gerekmez)
  4. XMLHttpRequest 1'i sinir bozucu yapan 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 değişken adını kullanan üç):

En basit:

this.response

Veya herhangi bir nedenle bind()bir sınıfa geri ararsanız:

e.target.response

Misal:

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

Veya (yukarıdakinin daha iyi anonim işlevler her zaman bir sorundur):

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

Daha kolay değil.

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

XMLHttpRequest gelişmiş özelliklerine göz atın

Tüm * modern tarayıcıları destekledi. Ve XMLHttpRequest 2 var olduğu için bu yaklaşımı kullandığımı doğrulayabilirim. Kullandığım tüm tarayıcılarda hiç sorun yaşamadım.

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

XMLHttpRequestDeğişken adını kullanmak başka bir büyük hatadır, çünkü onu kaybettiğinizde onload / oreadystatechange kapanışları içinde geri aramayı yürütmeniz 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 ... bu çok kısa bir işlev, ancak alıyor ve gönderiyor.

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 bir tam form öğesi ( document.getElementsByTagName('form')[0]) iletin :

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 senkronizasyonu uygulamadım ... bu kötü bir şey.

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


Yorumda belirtildiği gibi, error && synchronous kullanımı cevabın ana fikrini tamamen bozar. Ajax'ı doğru şekilde kullanmanın güzel ve kısa yolu hangisidir?

Hata işleyici

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, işlevi tehlikeye atmaması için statik olarak tanımlanmış bir hata işleyiciniz vardır. Hata işleyici diğer işlevler için de kullanılabilir.

Ancak bir hatadan gerçekten kurtulmanın tek yolu yanlış bir URL yazmaktır, bu durumda her tarayıcı bir hata atar.

Hata işleyicileri, özel başlıklar ayarlarsanız, responseType değerini blob dizi arabelleğine ayarlarsanız veya her neyse yararlı olabilir ...

Yöntem olarak 'POSTAPAPAP'ı geçseniz bile, bir hata vermez.

"Fdggdgilfdghfldj" yi formdata olarak geçirseniz bile bir hata vermez.

İlk durumda hata içindedir displayAjax()altındakiler this.statusTextolarak Method not Allowed.

İkinci durumda, basitçe çalışır. Doğru gönderi verilerini geçip geçmediğinizi sunucu tarafında kontrol etmelisiniz.

alanlar arası izin verilmiyor hata otomatik olarak atar.

Hata yanıtında herhangi bir hata kodu yoktur.

Yalnızca this.typehataya ayarlanmış olan vardır.

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

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

Not: İlk test olarak x ('x', displayAjax) yazdım ... ve tamamen bir yanıt aldı ... ??? Bu yüzden HTML'nin bulunduğu klasörü kontrol ettim ve 'x.xml' adlı bir dosya vardı. Yani XMLHttpRequest 2 dosyanızın uzantısını unutsanız bile BUNU BULACAKTIR . Ben LOL'ledim


Eşzamanlı bir dosya okuyun

Bunu yapma.

Tarayıcıyı bir süreliğine bloke etmek istiyorsanız, .txtsenkronize güzel bir büyük dosya 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 eşzamansız bir şekilde yapmanın başka bir yolu yoktur. (Evet, setTimeout döngüsüyle ... ama cidden mi?)

Diğer bir nokta ise ... API'lerle veya sadece kendi listenizin dosyalarıyla çalışıyorsanız veya her istek için her zaman farklı işlevleri kullanırsanız ...

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


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

İşlevi GENİŞLETMEK istiyorsanız ...

Evet yapabilirsin.

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

Ancak XMLHttpRequest 2 ile pek çok şey yapabilirsiniz:

Bir indirme yöneticisi yaptım (her iki tarafta özgeçmiş, dosya okuyucusu, dosya sistemi ile aralıklar kullanarak), tuval kullanarak çeşitli görüntü yeniden boyutlandırıcı dönüştürücüler, base64 görüntülerle web SQL veritabanlarını doldurma ve çok daha fazlası ... Ancak bu durumlarda yalnızca bunun için bir işlev oluşturmalısınız amaç ... bazen bir blob'a, dizi tamponlarına ihtiyaç duyarsınız, başlıkları ayarlayabilir, mime tipini geçersiz kılabilirsiniz ve çok daha fazlası vardır ...

Ancak buradaki soru, bir Ajax yanıtının nasıl döndürüleceğidir ... (Kolay bir yol ekledim.)

326 BenjaminGruenbaum May 12 2015 at 09:22

Söz kullanıyorsanız, bu cevap tam size göre.

Bu, AngularJS, jQuery (ertelenmiş), yerel XHR'nin değişimi (getirme), EmberJS, BackboneJS'nin kaydetme veya vaatleri döndüren herhangi bir düğüm kitaplığı anlamına gelir.

Kodunuz şu satırlarda 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, AJAX için geri aramalarla jQuery kullanan kişiler için bir cevap yazmakta iyi bir iş çıkardı. Yerel XHR için bir cevabım var. Bu cevap, ön uçta veya arka uçta vaatlerin genel kullanımı içindir.


Temel sorun

NodeJS / io.js ile tarayıcıdaki ve sunucudaki JavaScript eşzamanlılık modeli eşzamansız ve reaktiftir .

Bir promise döndüren bir yöntemi her çağırdığınızda, thenişleyiciler her zaman eşzamansız olarak, yani bir işleyicide olmayan altlarındaki koddan sonra çalıştırılır .then.

Bu araçlar size iade ederken henüz yürütülmediği tanımladığınız işleyicisi. Bu da, döndürdüğünüz değerin zaman içinde doğru değere ayarlanmadığı anlamına gelir.datathen

İş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

Değeri dataolan undefinedberi data = 5parçası henüz infaz edilmemiştir. Muhtemelen bir saniyede çalıştırılacak, ancak o zamana kadar döndürülen değerle ilgisi yok.

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

Bu soruna olası bir çözüm , programınıza hesaplama tamamlandığında ne yapacağını söyleyerek yeniden kodlama yapmaktır. Vaatler, doğası gereği zamansal (zamana duyarlı) olarak bunu aktif olarak sağlar.

Sözlerin hızlı bir özeti

Söz, zaman içindeki bir değerdir . Vaatlerin durumu vardır, hiçbir değeri olmayan beklemede olarak başlarlar ve şu noktalara yerleşebilirler:

  • yerine getirildi , hesaplamanın başarıyla tamamlandığı anlamına gelir.
  • reddedildi , hesaplamanın başarısız olduğu anlamına gelir.

Bir vaat, durumları yalnızca bir kez değiştirebilir ve bundan sonra daima aynı durumda kalır. thenDeğerlerini çıkarmak ve hataları işlemek için vaatlere işleyiciler ekleyebilirsiniz . thenişleyiciler aramaların zincirlenmesine izin verir . Sözler, onları döndüren API'ler kullanılarak oluşturulur . Örneğin, daha modern AJAX değişimi fetchveya jQuery'nin $.getiade vaatleri.

Bir .thensöze çağırdığımızda ve ondan bir şey iade ettiğimizde - işlenmiş 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 sorunu vaatlerle nasıl çözebiliriz bir bakalım. Öncelikle, bir delay işlevi oluşturmak için Promise yapıcısını kullanarak yukarıdan vaat durumları 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 kullanacak şekilde dönüştürdükten sonra, thenonu saymak için 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;
});

Temel olarak, bir dönen yerine değerini biz çünkü eşzamanlılık modeli yapamaz - bir geri dönüyoruz sarmalayıcı biz ki bir değeri paketini ile then. Birlikte açabileceğiniz bir kutu gibi then.

Bunu uyguluyorum

Bu, orijinal API çağrınız için aynıdır, şunları yapabilirsiniz:

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 da aynı şekilde çalışıyor. Zaten eşzamansız çağrılardan değer döndüremeyeceğimizi öğrendik, ancak işlemleri gerçekleştirmek için sözler kullanabilir ve bunları zincirleyebiliriz. Artık eşzamansız bir aramadan gelen yanıtı nasıl döndüreceğimizi biliyoruz.

ES2015 (ES6)

ES6 tanıtır jeneratörler orta dönmek ve sonra da vardı noktasını devam edebilirsiniz fonksiyonlardır. Bu tipik olarak 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;
}

Yinelenebilen dizi üzerinde bir yineleyici döndüren bir işlevdir 1,2,3,3,3,3,..... Bu kendi başına ilginç ve birçok olasılığa yer açsa da, belirli bir ilginç durum var.

Ürettiğimiz dizi sayılardan ziyade bir eylemler dizisiyse - bir eylem verildiğinde işlevi duraklatabilir ve işleve devam etmeden önce bekleyebiliriz. Dolayısıyla, bir dizi sayı yerine, bir dizi gelecekteki değerlere ihtiyacımız var - yani: vaatler.

Bu biraz zor ama çok güçlü numara, eşzamansız kodu eşzamanlı bir şekilde yazmamızı sağlar. Bunu sizin için yapan birkaç "koşucu" vardır, birini yazmak kısa birkaç satırlık koddur, ancak bu cevabın kapsamı dışındadır. Bluebird'leri Promise.coroutineburada kullanacağım , ancak coveya gibi başka sarmalayıcılar da var Q.async.

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 coroutinlerden tüketebileceğimiz bir vaadi kendisi döndürür. Ö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 standartlaştırılmıştır, şu anda birkaç teklif var ama hepsinde awaitsöz verebilirsiniz . Bu, yukarıdaki ES6 önerisi için asyncve awaitanahtar kelimelerini ekleyerek "şeker" (daha güzel sözdizimi) şeklindedir . 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
}

Yine de aynı şekilde bir söz verir :)

256 Nic May 23 2014 at 09:05

Ajax'ı yanlış kullanıyorsunuz. Buradaki fikir, herhangi bir şey döndürmesini sağlamak değil, bunun yerine verileri, verileri işleyen geri arama işlevi adı verilen bir şeye devretmektir.

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önderme işleyicisindeki herhangi bir şeyi döndürmek hiçbir şey yapmaz. Bunun yerine ya veriyi devretmeli ya da doğrudan başarı işlevi içinde verilerle istediğinizi yapmalısınız.

242 HemantBavle Feb 19 2014 at 01:58

En basit çözüm, bir JavaScript işlevi oluşturmak ve onu Ajax successgeri araması 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);    
}); 
229 JohannesFahrenkrug Aug 11 2016 at 21:17

Korkunç görünümlü, elle çizilmiş bir çizgi romanla cevaplayacağım. İkinci resim nedeni budur resultolduğunu undefinedsizin kod örneğinde.

165 MaleenAbewardana Aug 26 2014 at 15:11

Angular1

AngularJS kullanan kişiler için bu durumu kullanarak halledebilir Promises.

İşte diyor ki,

Sözler, eşzamansız işlevleri ayırmak için kullanılabilir ve birden çok işlevi birlikte zincirlemeye izin verir.

Burada da güzel bir açıklama bulabilirsiniz .

Aşağıda belirtilen belgelerde bulunan ö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.

Angular2 ve Daha Sonra

In Angular2aşağıdaki örnekte bakmak, ama onun ile önerilen kullanımına Observablessahip Angular2.

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

Orijinal gönderiye buradan bakın . Ancak Typescript yerel es6 Promises'i desteklemez , eğer onu kullanmak istiyorsanız, bunun için eklentiye ihtiyacınız olabilir.

Ayrıca burada söz olduğunu Spec burada tanımlar.

159 T.J.Crowder May 03 2017 at 23:59

Buradaki yanıtların çoğu, tek bir eşzamansız işleminiz olduğunda yararlı öneriler sunar, ancak bazen bu , bir dizideki veya başka bir liste benzeri yapıdaki her giriş için eşzamansız bir işlem yapmanız gerektiğinde ortaya çıkar . Bunu yapmak için cazip olan şey:

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

Misal:

// 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;
}

Bunun işe yaramamasının nedeni doSomethingAsync, sonuçları kullanmaya çalıştığınız zamana kadar gelen geri aramaların henüz çalışmamış olmasıdır .

Dolayısıyla, bir diziniz (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 (örtüşen) veya seri (sırayla birbiri ardına) yapın.

Paralel

Hepsini başlatabilir ve kaç geri arama beklediğinizi takip edebilir ve ardından bu kadar çok 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
        }
    });
});

Misal:

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

(Bunu ortadan kaldırabilir expectingve sadece kullanabilirdik results.length === theArray.length, ancak bu bizi theArrayaramalar beklemedeyken değişen olasılığa açık bırakır ...)

Kullandığımız nasıl Bildirimi indexdan forEachsonucu kurtarmak için resultssonuçlar sırasız gelen olsa bile, bu ilgilidir girdi olarak aynı pozisyonda (zaman uyumsuz aramalar sırayla tamamının olması gerekmez yapmak beri başlamış edildiği).

Peki ya bu sonuçları bir işlevden döndürmeniz gerekirse ? Diğer cevapların da işaret ettiği gibi, yapamazsınız; işlevinizin bir geri aramayı kabul etmesini ve çağırmasını (veya bir Sözü iade etmesini) sağlamanız gerekir . İş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);
});

Misal:

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 Promisebunun yerine a 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, doSomethingAsyncbize hatalar rejectyaparsak, bir hata aldığımızda sözümüzü reddederdik.)

Misal:

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

(Veya alternatif olarak, bunun için doSomethingAsyncbir söz döndüren bir sarmalayıcı yapabilir ve sonra aşağıdakileri yapabilirsiniz ...)

Eğer doSomethingAsyncsize verir Promise , şunları kullanabilirsiniz Promise.all:

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

doSomethingAsyncBunun ikinci ve üçüncü bir argümanı yok sayacağını biliyorsanız, onu doğrudan şuna iletebilirsiniz map( mapgeri aramasını üç argümanla çağırır, ancak çoğu insan çoğu zaman yalnızca ilkini kullanır):

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

Misal:

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

Unutmayın ki Promise.all, sözünü, hepsi çözüldüğünde verdiğiniz tüm sözlerin sonuçlarının bir dizisi ile çözer veya verdiğiniz sözlerden ilkini reddettiğinde sözünü reddeder.

Dizi

Diyelim ki operasyonların paralel olmasını istemiyorsunuz? Bunları birbiri ardına çalıştırmak istiyorsanız, bir sonrakine başlamadan önce her işlemin tamamlanmasını beklemeniz gerekir. İşte bunu yapan ve sonuçla bir geri aramayı çağıran bir işlev ö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);
});

(Çalışmayı seri olarak yaptığımız için results.push(result), sırayla sonuç almayacağımızı bildiğimiz için kullanabiliriz . Yukarıdakileri kullanabilirdik results[index] = result;, ancak aşağıdaki örneklerden bazılarında bir indeksimiz yok kullanmak.)

Misal:

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

(Ya da doSomethingAsyncbunun için size bir söz verir ve aşağıdakileri yapın ...)

Eğer doSomethingAsyncsize Sözü verir size (belki gibi bir transpiler ile ES2017 + sözdizimini kullanabilirsiniz eğer, Babil ), bir kullanabilir asyncfonksiyonu ile for-ofve 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);
});

Misal:

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 (henüz) kullanamıyorsanız, "Söz azaltma" modelinin bir varyasyonunu kullanabilirsiniz (bu, normal Promise indirgemesinden daha karmaşıktır çünkü sonucu birinden diğerine aktarmıyoruz, bunun yerine sonuçlarını bir dizide 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);
});

Misal:

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şlevleriyle daha az külfetli :

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

Misal:

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

113 FranciscoCarmona Jun 02 2016 at 15:31

Ş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, çözülmüş bir sözün geri dönmesi (geri döndüğünüzde çözülür ). Bu nedenle, $ http.get isteği tamamlanana kadar beklersiniz ve ardından console.log (res.joke) yürütülür (normal bir zaman uyumsuz akış olarak).res.data.value

Bu plnkr:

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

ES6 yolu (eşzamansız - bekleyin)

(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);
  });
})();
112 Alireza May 24 2017 at 16:38

Bu, birçok yeni JavaScript çerçevesinde kullanılan iki yollu veri bağlama veya depolama konseptinin sizin için harika çalışacağı yerlerden biridir ...

Dolayısıyla , Angular, React veya iki yolla veri bağlama veya depolama konsepti yapan başka bir çerçeve kullanıyorsanız , bu sorun sizin için basitçe düzeltilmiştir, bu nedenle basit bir deyişle undefined, sonucunuz ilk aşamadadır, bu nedenle result = undefined, veriler, ardından sonucu alır almaz güncellenecek ve Ajax çağrınızın yanıtının hangi yeni değere atanacağı ...

Ancak , örneğin bu soruda sorduğunuz gibi , saf javascript veya jQuery'de bunu nasıl yapabilirsiniz ?

Bir geri arama , vaat ve son zamanlarda gözlemlenebilir olanı sizin için ele almak için kullanabilirsiniz, örneğin, benzer bir fonksiyona sahip olduğumuz success()veya then()verileriniz sizin için hazır olduğunda uygulanacak olan sözlerde , gözlemlenebilir üzerindeki geri arama veya abone olma fonksiyonunda olduğu gibi .

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

Bu eşzamansız şeyleri yapmanın daha yeni yolları olan vaatler ve gözlemlenebilirler hakkında daha fazla bilgi için çalışma .

105 AnishK. Nov 01 2017 at 03:12

JavaScript'in 'gizemleri' ile uğraşırken karşılaştığımız çok yaygın bir sorundur. Bugün bu gizemi çözmeyi deneyeyim.

Basit bir JavaScript işleviyle başlayalım:

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

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

Bu basit bir eşzamanlı işlev çağrısıdır (her kod satırının sırayla bir sonrakinden önce 'işiyle bittiği') ve sonuç beklendiği gibi aynıdır.

Şimdi, fonksiyonumuza biraz gecikme ekleyerek biraz bükülme ekleyelim, böylece tüm kod satırları sırayla 'bitmiş' olmaz. Böylece, işlevin eşzamansız davranışını taklit edecektir:

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

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

İşte buyrun, bu gecikme beklediğimiz işlevselliği bozdu! Ama tam olarak ne oldu? Koda bakarsanız aslında oldukça mantıklı. işlev foo(), çalıştırıldığında hiçbir şey döndürmez (bu nedenle döndürülen değerdir undefined), ancak bir zamanlayıcı başlatır ve 1 saniye sonra 'wohoo' döndürmek için bir işlevi çalıştırır. Ama gördüğünüz gibi, bar'a atanan değer, foo () 'dan hemen döndürülen şeylerdir, yani hiçbir şey değildir undefined.

Peki bu sorunu nasıl çözeceğiz?

Fonksiyonumuza bir SÖZ VERME soralım . Söz gerçekten ne anlama geldiğiyle ilgilidir: bu, işlevin gelecekte alacağı herhangi bir çıktıyı sağlamanızı garanti ettiği anlamına gelir. Öyleyse yukarıdaki küçük sorunumuz için iş başında 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 resolve, değere (geri dönmek istediğiniz) bir söz kullanabilirsiniz . Böylece, kısaca , asenkron işlevlerde döndürmek yerine değeri çözümlemiş olursunuz .

GÜNCELLEME (Eşzamansız / bekleme ile vaatler)

Sözlerle then/catchçalışmak için kullanmanın dışında, bir yaklaşım daha var. Buradaki fikir, eşzamansız bir işlevi tanımak ve ardından bir sonraki kod satırına geçmeden önce sözlerin çözülmesini beklemektir . Hala promiseskaputun altında, ancak farklı bir sözdizimsel yaklaşımla. İşleri daha net hale getirmek için aşağıda bir karşılaştırma bulabilirsiniz:

then / catch version:

function saveUsers(){
     getUsers()
      .then(users => {
         saveSomewhere(users);
      })
      .catch(err => {
          console.error(err);
       })
 }

eşzamansız / bekleme sürümü:

  async function saveUsers(){
     try{
        let users = await getUsers()
        saveSomewhere(users);
     }
     catch(err){
        console.error(err);
     }
  }
101 jsbisht Sep 02 2015 at 19:54

Eşzamansız bir işlevden bir değer döndürmenin diğer bir yaklaşımı, eşzamansız işlevin sonucunu depolayacak bir nesneyi iletmektir.

İşte bunun bir örneği:

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

resultEşzamansız işlem sırasında değeri depolamak için nesneyi kullanıyorum . Bu, sonucun eşzamansız işten sonra bile kullanılabilir olmasını sağlar.

Bu yaklaşımı çok kullanıyorum. Ardışık modüller aracılığıyla sonucu geri kablolamanın dahil olduğu durumlarda bu yaklaşımın ne kadar iyi çalıştığını bilmek isterim.

89 rohithpr Jan 26 2016 at 00:43

Sözler ve geri aramalar birçok durumda iyi sonuç verirken, aşağıdaki gibi bir şeyi ifade etmek arkada bir acıdır:

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

Sonunda geçecektin async1; nametanımsız olup olmadığını kontrol edin ve buna göre geri aramayı arayın.

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

async1(name, async2)

Öyle olsa tamam küçük örneklerde sen katılan benzer durumlarda ve hata işleme çok şey var zaman can sıkıcı olur.

Fibers sorunu çözmede 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 buradan kontrol edebilirsiniz .

88 loretoparisi Apr 13 2016 at 05:55

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

  • Eşzamansız HTTP çağrılarını işleyin;
  • Her API çağrısından yanıt bekleyin;
  • Promise kalıbını kullanın ;
  • Birden çok HTTP çağrısına katılmak için Promise.all modelini kullanın ;

Bu çalışma örneği bağımsızdır. Çağrı XMLHttpRequestyapmak için pencere nesnesini kullanan basit bir istek nesnesi tanımlayacaktır . Bir grup sözün tamamlanmasını beklemek için basit bir işlev tanımlayacaktır.

Bağlam. Örnek, belirli bir sorgu dizesi kümesi için nesneleri aramak için Spotify Web API uç noktasını playlistsorgulamaktır:

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

Her öğe için, yeni bir Promise bir blok ExecutionBlockbaşlatır - sonucu ayrıştırır, sonuç dizisine, yani Spotify usernesnelerinin bir listesine dayalı olarak yeni bir vaatler dizisi planlar ve yeni HTTP çağrısını ExecutionProfileBlockeşzamansız olarak yürütür .

Daha sonra, birden çok ve tamamen eşzamansız iç içe geçmiş HTTP çağrısı oluşturmanıza ve her çağrı alt kümesinden gelen sonuçları birleştirmenize olanak tanıyan iç içe geçmiş bir Promise yapısı görebilirsiniz Promise.all.

NOT Son Spotify searchAPI'leri, istek başlıklarında bir erişim belirtecinin belirtilmesini gerektirecektir:

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

Bu nedenle, aşağıdaki örneği çalıştırmanız için erişim jetonunuzu 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ü burada kapsamlı bir şekilde tartıştım .

84 PabloMatiasGomez Apr 22 2016 at 21:47

Kısa cevap, şöyle bir geri arama uygulamanız gerekir:

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

$.ajax({
    url: "...",
    success: callback
});
82 mikemaccana Jun 02 2017 at 16:51

2017 cevabı: artık her mevcut tarayıcıda ve düğümde tam olarak istediğinizi yapabilirsiniz

Bu oldukça basit:

  • Söz Ver
  • JavaScript'e bir değere (HTTP yanıtı gibi) çözülme vaadini beklemesini söyleyen 'await' seçeneğini kullanın.
  • 'Eşzamansız' anahtar kelimeyi üst işleve ekleyin

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

(async function(){

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

})()

await, tüm mevcut tarayıcılarda ve düğüm 8'de desteklenmektedir

80 AniketJha Feb 03 2018 at 13:06

Js, tek iş parçacıklıdır.

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

1) Etkinlik Döngüsü

2) Web API

3) Olay Sırası

Olay Döngüsü sonsuza kadar çalışır, yani sonsuz döngüdür.Olay Kuyruğu, tüm işlevinizin bazı olaylara (örneğin: tıklama) itildiği yerdir; bu, sıradan teker teker gerçekleştirilir ve bu işlevi çalıştıran ve kendi kendini hazırlayan Olay döngüsüne yerleştirilir. Bu, bir işlevin çalıştırılmasının, işlev kuyruktaki işlev olay döngüsünde yürütülmeden önce başlamadığı anlamına gelir.

Şimdi bir kuyrukta iki işlevi ittiğimizi düşünelim, biri sunucudan bir veri almak için, diğeri bu verileri kullanmak için. ServerRequest () işlevini önce queue'da, sonra utiliseData () işlevini ittik. serverRequest işlevi olay döngüsüne girer ve sunucudan veri almanın ne kadar zaman alacağını asla bilemediğimiz için sunucuya bir çağrı yapar, bu nedenle bu işlemin zaman alması beklenir ve bu nedenle olay döngümüzü meşgul ederiz, böylece sayfamızı asarız, işte burada Web API, bu işlevi olay döngüsünden alır ve sunucunun olay döngüsünü serbest bırakmasıyla ilgilenir, böylece kuyruktaki bir sonraki işlevi çalıştırabiliriz. Kuyruktaki bir sonraki işlev, döngüye giren, ancak kullanılabilir veri olmadığından, Bir sonraki işlevin boşa harcanması ve çalıştırılması kuyruğun sonuna kadar devam eder. (Buna Async çağrı denir, yani verileri alana kadar başka bir şey yapabiliriz)

ServerRequest () fonksiyonumuzun bir kodda bir return ifadesi olduğunu varsayalım, sunucudan veri aldığımızda, Web API, kuyruğun sonunda onu kuyruğa itecektir. Kuyruğun sonunda itildiğinden, kuyruğumuzda bu verileri kullanmak için herhangi bir işlev kalmadığından verilerini kullanamayız. Bu nedenle Async Call'dan bir şey döndürmek mümkün değildir.

Bu nedenle buna Çözüm, geri arama veya sözdür .

Buradaki cevaplardan birinden bir resim, Geri arama kullanımını doğru bir şekilde açıklıyor ... Fonksiyonumuzu (sunucudan döndürülen verileri kullanan fonksiyon) çağrı sunucusuna fonksiyon veriyoruz.

 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

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

Javscript.info geri arama

70 VinothRajendran May 26 2016 at 20:26

Uzaktan arama yapmak için bu özel kitaplığı (Promise kullanılarak yazılmıştır) 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)
});
70 amaksr May 27 2017 at 09:47

Diğer bir çözüm, kodun sıralı çalıştırıcı nsynjs aracılığıyla yürütülmesidir .

Altta yatan işlev vaat edilirse

nsynjs, tüm vaatleri sırayla değerlendirecek ve vaat sonucunu datamülke yerleştirecektir:

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>

Altta yatan işlev vaat edilmemişse

Adım 1. Geri aramayla birlikte işlevini nsynjs-duyarlı sarmalayıcıya sarın (vaat edilmiş bir sürüme sahipse, 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. Senkron mantığı devreye alın:

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

Adım 3. İşlevi nsynjs aracılığıyla eşzamanlı olarak çalıştırın:

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

Nsynjs, tüm işleçleri ve ifadeleri adım adım değerlendirecek ve yavaş bir işlevin sonucunun hazır olmaması durumunda yürütmeyi duraklatacaktır.

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

42 James Feb 17 2018 at 22:26

ECMAScript 6, eşzamansız bir tarzda kolayca programlama yapmanızı sağlayan 'üreteçlere' 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 yaparsınız:

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 gerekiyorsa, ECMAScript 5'i oluşturmak için kodu Babel veya kapatma derleyicisi aracılığıyla çalıştırabilirsiniz.

Geri çağırma ...argsbir diziye sarılır ve bunları okuduğunuzda yok edilir, böylece desen birden çok bağımsız değişkeni olan geri aramalarla baş edebilir. Örneğin fs düğümü ile :

const [err, data] = yield fs.readFile(filePath, "utf-8", callback);
39 MohanDere Aug 13 2016 at 16:36

Eşzamansız isteklerle çalışmak için bazı yaklaşımlar şunlardır:

  1. Tarayıcı Promise nesnesi
  2. S - JavaScript için bir söz kitaplığı
  3. A + Promises.js
  4. jQuery ertelendi
  5. XMLHttpRequest API
  6. Geri arama kavramını kullanma - İlk yanıtta uygulama olarak

Örnek: jQuery, uygulamayı birden çok istekle çalışması için erteledi

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

38 4revs,3users92%user663031 Jan 23 2016 at 10:28

Kendimizi "zaman" dediğimiz boyutta ilerliyor gibi görünen bir evrende buluyoruz. Zamanın ne olduğunu gerçekten anlamıyoruz, ancak bunun hakkında akıl yürütmemize ve konuşmamıza izin veren soyutlamalar ve sözcükler geliştirdik: "geçmiş", "şimdi", "gelecek", "önce", "sonra".

İnşa ettiğimiz bilgisayar sistemleri - gittikçe daha fazla - önemli bir boyut olarak zamana sahip. Gelecekte bazı şeyler olacak şekilde ayarlanmıştır. Sonunda bu ilk şeyler gerçekleştikten sonra başka şeylerin de olması gerekir. Bu, "eşzamansızlık" denen temel kavramdır. Gittikçe ağlaşan dünyamızda, en yaygın eşzamansızlık durumu, bazı uzak sistemlerin bazı taleplere yanıt vermesini beklemektir.

Bir örnek düşünün. Sütçüyü ara ve biraz süt sipariş et. Geldiğinde kahvenize koymak istersiniz. Sütü şu anda kahvenize koyamazsınız çünkü henüz burada değil. Kahvenize koymadan önce gelmesini beklemelisiniz. Başka bir deyişle, aşağıdakiler işe yaramayacaktır:

var milk = order_milk();
put_in_coffee(milk);

JS o gerektiğini bilmenin bir yolu olmadığı için beklemek için order_milkçalışmasından önce sona put_in_coffee. Başka bir deyişle, bunun asenkron olduğunu bilmez - bu order_milk, gelecekteki bir zamana kadar sütle sonuçlanmayacak bir şeydir. JS ve diğer bildirime dayalı diller, bir ifadeyi beklemeden birbiri ardına yürütür.

Bu soruna klasik JS yaklaşımı, JS'nin, iletilebilen birinci sınıf nesneler olarak işlevleri desteklemesi gerçeğinden yararlanarak, bir işlevi eşzamansız isteğe bir parametre olarak geçirmektir; gelecekte bir ara görevi. Bu "geri arama" yaklaşımıdır. Şuna benziyor:

order_milk(put_in_coffee);

order_milkbaşlar, sütü emreder, sonra, ne zaman ve ancak geldiğinde, çağırır put_in_coffee.

Bu geri arama yaklaşımındaki sorun, sonucunu bildiren bir işlevin normal anlamını kirletmesidir return; bunun yerine işlevler, parametre olarak verilen bir geri aramayı çağırarak sonuçlarını bildirmemelidir. Ayrıca, bu yaklaşım, daha uzun olay dizileriyle uğraşırken hızla hantal hale gelebilir. Örneğin sütün kahveye konmasını bekledikten sonra ancak o zaman üçüncü bir adım, yani kahveyi içmek istiyorum diyelim. Sonunda şöyle bir şey yazmaya ihtiyacım var:

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

put_in_coffeeHem içine koymak için sütü , hem de süt konulduktan sonra drink_coffeeyürütmek için eylemi ( ) geçiyorum . Bu kodun yazılması, okunması ve hata ayıklaması zorlaşıyor.

Bu durumda, sorudaki kodu şu şekilde yeniden yazabiliriz:

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

Sözleri girin

Bu, bir geleceği veya bir tür eşzamansız sonucu temsil eden belirli bir değer türü olan "vaat" kavramının motivasyonuydu . Zaten olmuş veya gelecekte olacak veya hiç olmayacak bir şeyi temsil edebilir. Vaatlerin then, vaatin temsil ettiği sonuç gerçekleştiğinde yürütülecek bir eylemi ilettiğiniz, adlandırılmış tek bir yöntemi vardır.

Süt ve kahvemiz order_milksöz konusu olduğunda, gelen süt için bir söz vermeyi tasarlıyoruz ve ardından aşağıdaki put_in_coffeegibi bir theneylem olarak belirtiyoruz:

order_milk() . then(put_in_coffee)

Bunun bir avantajı, gelecekteki oluşumların sıralarını oluşturmak için bunları bir araya getirebilmemizdir ("zincirleme"):

order_milk() . then(put_in_coffee) . then(drink_coffee)

Özel probleminize sözler uygulayalım. İstek mantığımızı, bir vaat döndüren bir işlevin içine yerleştireceğiz:

function get_data() {
  return $.ajax('/foo.json');
}

Aslında, yaptığımız tek şey returnaramaya bir eklemek oldu $.ajax. Bu işe yarıyor çünkü jQuery $.ajaxzaten bir tür vaat benzeri bir şey döndürüyor. (Pratikte, ayrıntılara girmeden, gerçek bir söz vermek için bu çağrıyı tamamlamayı veya buna alternatif bir alternatif kullanmayı tercih $.ajaxederiz.) Şimdi, dosyayı yüklemek ve bitmesini beklemek istiyorsak ve o zaman bir şeyler yap, basitçe söyleyebiliriz

get_data() . then(do_something)

Örneğin,

get_data() . 
  then(function(data) { console.log(data); });

thenSözleri kullandığımızda, birçok işlevi içeri aktarırız, bu nedenle daha kompakt ES6 tarzı ok işlevlerini kullanmak genellikle yararlı olur:

get_data() . 
  then(data => console.log(data));

asyncanahtar kelime

Ancak, eşzamanlıysa tek yönlü ve eşzamansızsa oldukça farklı bir şekilde kod yazmak zorunda kalmanın hala belirsiz bir şekilde tatmin edici olmayan bir yanı var. Senkronize için yazıyoruz

a();
b();

ama aeşzamansızsa, sözler yazmalıyız

a() . then(b);

Yukarıda, "JS'nin, ikinciyi çalıştırmadan önce ilk çağrının bitmesini beklemesi gerektiğini bilmesinin bir yolu yoktur" demiştik . Orada eğer hoş olmaz idi o JS anlatmak için bir yol? await"Eşzamansız" işlev adı verilen özel bir işlev türü içinde kullanılan anahtar kelime olduğu ortaya çıktı . Bu özellik ES'nin gelecek sürümünün bir parçasıdır, ancak doğru ön ayarlarla birlikte Babel gibi aktarıcılarda zaten mevcuttur. Bu, basitçe yazmamızı sağlar

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

Senin durumunda, şöyle bir şey yazabilirsin

async function foo() {
  data = await get_data();
  console.log(data);
}
37 DavidRTribble Sep 24 2015 at 05:52

Kısa cevap : foo()Yönteminiz hemen dönerken , işlev döndükten sonra$ajax() çağrı eşzamansız olarak yürütülür . Sorun, döndüğünde zaman uyumsuz çağrı tarafından alınan sonuçların nasıl ve nerede saklanacağıdır.

Bu ileti dizisinde çeşitli çözümler verilmiştir. Belki de en kolay yol, bir nesneyi foo()yönteme iletmek ve zaman uyumsuz çağrı tamamlandıktan sonra sonuçları o 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

Çağrının foo()yine de yararlı bir şey döndürmeyeceğini unutmayın . Ancak, zaman uyumsuz çağrının sonucu şimdi içinde saklanacaktır result.response.

36 MahfuzurRahman Apr 24 2017 at 15:09

Başarının callback()içinde bir işlev kullanın foo(). Bu şekilde deneyin. Anlaşılması basit ve 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();
30 AmirFo Dec 07 2018 at 21:10

Promise Kullanmak

Bu sorunun en mükemmel cevabı kullanmaktır Promise.

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...!

Sözleri kullanmakla ilgili bir sorun var!

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

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

Uncaught ReferenceError: Promise is not defined

Bu yüzden ES3 için kendi Promise sınıfımı, tanımlanmamışsa aşağıdaki js derleyicilerine uygulamaya karar verdim . Bu kodu ana kodunuzun önüne ekleyin ve ardından güvenle Promise'ı kullanın!

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;
    }();
}
29 PieterJanBonestroo Jan 14 2018 at 02:13

Soru şuydu:

Eşzamansız bir aramadan gelen yanıtı nasıl döndürürüm?

hangisi şu şekilde yorumlanabilir:

Nasıl yapmak için asenkron kod bakmak senkron ?

Çö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 ile yazılabilse de Python'da yazmayı ve Transcrypt kullanarak Javascript'e derlemeyi tercih ediyorum . Yeterince açık olacak.)

Öncelikle JQuery kullanımını etkinleştirip şu şekilde $kullanılabilir hale gelelim S:

__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()

Kullanım asenkron o sanki kodu senkron :

async def readALot():
    try:
        result1 = await read("url_1")
        result2 = await read("url_2")
    except Exception:
        console.warn("Reading a lot failed")
28 KhoaBui Jul 06 2017 at 03:28

Elbette eşzamanlı istek, söz gibi birçok yaklaşım var, ancak deneyimlerime göre geri arama yaklaşımını kullanmanız gerektiğini düşünüyorum. Javascript'in eşzamansız davranışı doğaldır. Dolayısıyla, kod pasajınız biraz farklı şekilde yeniden yazılabilir:

function foo() {
    var result;

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

    return result;
}

function myCallback(response) {
    // Does something.
}
26 SanjiMika Jan 20 2020 at 05:23

Buradaki tüm yanıtları ve deneyimlerimi okuduktan sonra callback, promise and async/await, JavaScript'teki asenkron programlamanın detayına devam etmek istiyorum .

1) Geri Arama: Geri aramanın temel nedeni, bir olaya yanıt olarak kod çalıştırmaktır (aşağıdaki örneğe bakın). Her seferinde JavaScript'te geri arama kullanıyoruz.

const body = document.getElementsByTagName('body')[0];
function callback() {
  console.log('Hello');
}
body.addEventListener('click', callback);

Ancak, aşağıdaki örnekte çok sayıda iç içe geçmiş geri çağırma kullanmanız gerekiyorsa, kod yeniden düzenleme için korkunç olacaktır.

asyncCallOne(function callback1() {
  asyncCallTwo(function callback2() {
    asyncCallThree(function callback3() {
        ...
    })
  })
})

2) Promise: Bir sözdizimi ES6 - Promise geri arama cehennemi sorununu çözer!

const myFirstPromise = new Promise((resolve, reject) => {
  // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
  // In this example, we use setTimeout(...) to simulate async code. 
  // In reality, you will probably be using something like XHR request or an HTML5 API.
  setTimeout(() => {
    resolve("Success!")  // Yay! Everything went well!
  }, 250)
}) 

myFirstPromise
  .then((res) => {
    return res.json();
  })
  .then((data) => {
    console.log(data);
  })
  .catch((e) => {
    console.log(e);
  });

myFirstPromise, eşzamansız kod sürecini temsil eden bir Promise örneğidir. Çözümleme işlevi, Promise örneğinin bittiğini gösterir. Daha sonra, vaat örneğinde .then () (istediğiniz gibi. Sonra zinciri) ve .catch () çağırabiliriz:

then — Runs a callback you pass to it when the promise has fulfilled.
catch — Runs a callback you pass to it when something went wrong.

3) Async / Await : yeni bir sözdizimi ES6 - Await, temelde Promise'in şeker sözdizimidir!

Zaman uyumsuz işlev, vaatlerle elde edeceğimiz sonucu elde etmek için daha az kod yazmamızı sağlayan temiz ve kısa bir sözdizimi sağlar. Async / Await, eşzamanlı koda benzer ve eşzamanlı kodun okunması ve yazılması çok daha kolaydır. Async / Await ile hataları yakalamak için bloğu kullanabiliriz try...catch. Burada, Promise sözdiziminden bir .sonra () zinciri yazmanıza gerek yoktur.

const getExchangeRate = async () => {
  try {
    const res = await fetch('https://getExchangeRateData');
    const data = await res.json();
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

getExchangeRate();

Sonuç: Bunlar, JavaScript'te eşzamansız programlama için iyi anlamanız gereken tamamen üç sözdizimidir. Bu nedenle, mümkünse, eşzamansız kodlarınızı yeniden düzenlemek için "promise" veya "async / await" kullanmanızı tavsiye ederim (çoğunlukla XHR istekleri için) !

20 MatthewBrent May 04 2018 at 22:56

Size kod atmak yerine, JS'nin geri aramaları ve zaman uyumsuzluğu nasıl işlediğini anlamanın anahtarı olan 2 kavram vardır. (bu bir kelime mi?)

Etkinlik Döngüsü ve Eş Zamanlılık Modeli

Bilmeniz gereken üç şey var; Kuyruk; olay döngüsü ve yığın

Geniş, basit terimlerle, olay döngüsü proje yöneticisi gibidir, çalıştırmak isteyen ve kuyruk ile yığın arasında iletişim kuran herhangi bir işlevi sürekli olarak dinler.

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

Bir şeyi çalıştırmak için bir mesaj aldığında, onu kuyruğa ekler. Sıra, yürütülmeyi bekleyen şeylerin listesidir (AJAX isteğiniz gibi). bunu şöyle hayal edin:

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

Bu mesajlardan biri yürütüldüğünde, mesajı kuyruktan çıkarır ve bir yığın oluşturur, yığın, JS'nin mesajdaki talimatı gerçekleştirmek için yürütmesi gereken her şeydir. Yani bizim örneğimizde aramak için söylendifoobarFunc

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

Yani foobarFunc'ın yürütmesi gereken her şey (bizim durumumuzda anotherFunction) yığına itilecektir. çalıştırılır ve sonra unutulur - olay döngüsü daha sonra kuyruktaki bir sonraki şeye geçer (veya mesajları dinler)

Buradaki kilit nokta, yürütme emridir. Yani

NE ZAMAN bir şey koşacak

AJAX kullanarak harici bir tarafa çağrı yaptığınızda veya herhangi bir asenkron kodu çalıştırdığınızda (örneğin bir setTimeout), Javascript devam etmeden önce bir yanıta bağımlıdır.

Asıl soru, cevabı ne zaman alacak? Cevap bilmiyoruz - bu yüzden olay döngüsü bu mesajın "hey beni çalıştır" demesini bekliyor. JS, bu mesajı eşzamanlı olarak beklediyse, uygulamanız donacak ve emecektir. Bu nedenle JS, mesajın sıraya geri eklenmesini beklerken sıradaki bir sonraki öğeyi yürütmeye devam eder.

Bu nedenle, eşzamansız işlevsellikle geri arama adı verilen şeyler kullanırız . Kelimenin tam anlamıyla bir söz gibi . Bir noktada bir şeyi geri vereceğime söz veriyorum jQuery deffered.done deffered.failve deffered.always(diğerleri arasında) adı verilen belirli geri aramaları kullanır . Hepsini burada görebilirsin

Yani yapmanız gereken şey, kendisine aktarılan verilerle bir noktada çalıştırılması vaat edilen bir işlevi iletmektir.

Bir geri arama hemen yürütülmediğinden, ancak daha sonra başvuruyu çalıştırmadığı işleve iletmek önemlidir. yani

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

böylece çoğu zaman (ama her zaman değil) size geçmek olacak foodeğilfoo()

Umarım bu biraz mantıklı olacaktır. Bunun gibi kafa karıştırıcı şeylerle karşılaştığınızda - en azından bir anlayışa sahip olmak için belgeleri tamamen okumanızı şiddetle tavsiye ederim. Sizi çok daha iyi bir geliştirici yapacak.