Soru Yanıtı eşzamansız bir çağrıdan nasıl geri döndürebilirim?


Benim bir fonksiyonum var foo bir Ajax isteği yapar. Yanıtı nasıl geri verebilirim? foo?

Değeri success Geri arama yanı sıra işlev içinde bir yerel değişkene yanıt atama ve bunu geri, ancak bu yollardan hiçbiri aslında yanıtı döndürmez.

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

4321
2018-01-08 17:06


Menşei


@MaximShoustin Sadece bir javascript ekledim (jQuery yok) ama Felix'in dediği gibi - "Bir AJAX çağrısından cevabı nasıl geri döndürüyorum", "eşzamanlılık JavaScript'te nasıl çalışır?" Demek için gerçekten iyi bir yoldur. Angular'dan bahsetme $httpjQuery’ye çok benzer $.ajax neyse bazı küçük farklılıklar ile (sindirim döngüsü ile interoping gibi). - Benjamin Gruenbaum
Yapma kullanım async: false. Tarayıcıyı donduracak ve kullanıcıları uzaklaştıracak. okumak blog.slaks.net/2015-01-04/async-method-patterns Eşzamansız işlemler ile nasıl çalışılacağını öğrenmek. - SLaks
Bunu, ilk parametrenin özniteliği olarak async: false ekleyerek yaptınız gibi mümkündür. Cevabımı aşağıya bakın. @tvanfosson bu çözümü Stackoverflow'ta verdi. - arrowman
bu mey sana yardım et codingbin.com/get-return-data-ajax-call - Manoj Kumar
@SLaks Birisi lütfen bunu AirAsia.com adresindeki web sitesi "rock star geliştiricileri" na iletin. Bu async: niçin asla kullanılmamasının harika bir örnek olay çalışması (birkaç saniye süren ajax çağrılarıyla geliştirildi). - Mâtt Frëëman


Cevaplar:


-> Farklı örneklerle eşzamanlı davranışın daha genel bir açıklaması için lütfen bkz.  Bir işlevin içinde değiştirdikten sonra değişkeni neden değişmez? - Eşzamansız kod referansı 

-> Sorunu zaten biliyorsanız, aşağıdaki olası çözümlere geçin.

Sorun

bir içinde ajax anlamına gelir eşzamanlı olmayan . Bu, talebi göndermek (veya yanıtı almak) normal yürütme akışından çıkarılmak demektir. Örneğinde, $.ajax hemen döner ve bir sonraki ifade, return result;olarak geçtiğiniz işlevden önce yürütülür success geri arama bile çağrıldı.

İşte, senkron ve asenkron akış temizleyici arasındaki farkı umarız kılan bir benzetme:

Senkron

Bir arkadaşınıza bir telefon görüşmesi yaptığınızı ve sizin için bir şeyler aramasını isteyin. Bir süre sürebilirse de, telefonunuzda bekler ve arkadaşınıza ihtiyacınız olan cevabı verene kadar uzaya bakarsınız.

"Normal" kodu içeren bir işlev çağrısı yaptığınızda da aynı şey gerçekleşir:

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

var item = findItem();

// Do something with item
doSomethingElse();

Buna rağmen findItem yürütmek için uzun zaman alabilir, sonra gelen herhangi bir kod var item = findItem(); zorunda Bekleyin işlev sonucu döndürene kadar.

eşzamanlı olmayan

Aynı sebepten dolayı arkadaşını tekrar ara. Ama bu sefer ona aceleniz olduğunu söylemelisiniz. seni geri ara Cep telefonunuzda. Takıl, evi terk et ve yapmayı planladığın şeyi yap. Arkadaşın seni geri aradığında, sana verdiği bilgilerle ilgileniyorsun.

Tam olarak bir Ajax isteğinde bulunduğun sırada oluyor.

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

Yanıtı beklemek yerine, yürütme hemen devam eder ve Ajax çağrısı gerçekleştirildikten sonra ifade devam eder. Yanıtı en sonunda almak için, yanıt alındıktan sonra çağrılacak bir işlev sağlarsınız. geri aramak (bir şey fark ettin mi? geri aramak ?). Bu çağrıdan sonra gelen herhangi bir deyim, geri arama çağrılmadan önce yürütülür.


Çözüm (ler)

JavaScript'in eşzamansız doğasını kucaklayın! Belirli senkronize olmayan işlemler senkronize benzerler sağlasa da (yani "Ajax"), özellikle tarayıcı bağlamında bunları kullanmak kesinlikle önerilmez.

Neden bu kadar soruyorsun?

JavaScript, tarayıcının UI iş parçacığında çalışır ve uzun süre çalışan bir işlem UI'yi kilitler ve yanıt vermez. Ayrıca, JavaScript için yürütme süresinde bir üst sınır vardır ve tarayıcı kullanıcıya yürütmeyi sürdürüp sürdürmeyeceğini soracaktır.

Bütün bunlar gerçekten kötü bir kullanıcı deneyimi. Kullanıcı, her şeyin iyi çalışıp çalışmadığını söyleyemeyecektir. Ayrıca, yavaş bağlantı olan kullanıcılar için etki daha da kötü olacaktır.

Aşağıda, birbirlerinin üzerine inşa eden üç farklı çözüm ele alacağız:

  • İle söz veriyor async/await (ES2017 +, bir transpiler veya rejeneratör kullanıyorsanız eski tarayıcılarda kullanılabilir)
  • Callbacks (düğümünde popüler)
  • İle söz veriyor then() (ES2015 +, birçok kitaplık kitaplığından birini kullanırsanız eski tarayıcılarda kullanılabilir)

Her üçü de mevcut tarayıcılarda ve 7+ düğümünde kullanılabilir. 


ES2017 +: Vaat ediyor async/await

2017'de piyasaya sürülen yeni ECMAScript sürümü tanıtıldı sözdizimi düzeyinde destek Eşzamansız fonksiyonlar için. Yardımıyla async ve await, "senkronize bir tarzda" eşzamansız yazabilirsiniz. Yine de hata yapmayın: Kod hala senkronize değildir, ancak okumak / anlamak daha kolaydır.

async/await sözlerin üstüne inşa edilir: async işlevi her zaman bir söz verir. await Bir söz "isteksiz" ve ya vaadin çözüldüğü değerle sonuçlanır ya da vaat reddedilirse bir hata atar.

Önemli: Sadece kullanabilirsiniz await içinde async işlevi. Bu, en üst düzeyde, hala sözle doğrudan çalışmak zorunda olduğunuz anlamına gelir.

Hakkında daha fazla okuyabilirsiniz async ve await MDN'de.

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 a second (just for the sake of this example)
    await delay(1000);
    // 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;
  }
}

// Async functions always return a promise
getAllBooks()
  .then(function(books) {
    console.log(books);
  });

Daha yeni tarayıcı ve düğüm sürümleri destek async/await. Ayrıca kodunuzu ES5'e dönüştürerek eski ortamları destekleyebilirsiniz. rejeneratör (veya rejeneratör gibi Babil).


Fonksiyonları kabul edelim geri aramaları

Geri arama sadece başka bir işleve iletilen bir işlevdir. Diğer işlev, hazır olduğunda iletilen işlevi çağırabilir. Eşzamansız bir süreç bağlamında, asenkron işlem her ne zaman yapılırsa geri arama çağrılır. Genellikle sonuç geri dönüşüme geçirilir.

Sorunun örneğinde, yapabilirsiniz foo geri aramayı kabul et ve kullan success geri aramak. 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, ancak herhangi bir fonksiyon referansını geçebilirsiniz:

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

foo(myCallback);

foo kendisi şöyle tanımlanır:

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

callback geçtiğimiz işleve başvururuz foo biz onu çağırdığımızda success. Yani Ajax talebi başarılı olduğunda, $.ajax Arayacağım callback ve yanıtı geri aramaya iletin ( result, çünkü bu geri dönüşümü nasıl tanımladık?).

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 göründüğünden daha fazla kod yazmak daha kolaydır. Sonuçta, tarayıcıdaki JavaScript yoğun olay odaklı (DOM olayları). Ajax yanıtını almak bir olaydan başka bir şey değildir.
Üçüncü taraf koduyla çalışmanız gerektiğinde zorluklar ortaya çıkabilir, ancak çoğu sorun sadece uygulama akışı üzerinden düşünülerek çözülebilir.


ES2015 +: Vaat ediyor sonra()

Promise API'sı ECMAScript 6'nın yeni bir özelliğidir (ES2015), ancak iyi tarayıcı desteği zaten. Standart Promises API'yi uygulayan ve asenkron işlevlerin kullanımını ve bileşimini kolaylaştırmak için ek yöntemler sağlayan birçok kütüphane de vardır (ör. Mavikuş).

Sözler için konteynerler gelecek değerler. Söz, değeri aldığında (bu kararlı) veya iptal edildiğinde (reddedilen), bu değere erişmek isteyen tüm "dinleyicilerini" haberdar eder.

Düz geri aramalar üzerindeki avantaj, kodunuzu kod çözmenize izin vermeleri ve oluşturmaları daha kolay olmalarıdır.

İşte bir söz kullanmak için basit bir örnek:

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 uygulanan sözler şöyle olabilir:

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 yazıyorsanız, bunları ciddi olarak düşünmelisiniz. Büyük bir soyutlama ve kodunuzun ayrılmasını sağlarlar.

Sözler hakkında daha fazla bilgi: HTML5 kayalar - JavaScript Sözleri

Yan not: jQuery'nin ertelenmiş nesneleri

Ertelenmiş nesneler jQuery'nin vaatlerin özel uygulaması (Promise API'sı standartlaştırılmadan önce). Neredeyse sözler gibi davranıyorlar ama biraz farklı bir API ortaya koyuyorlar.

JQuery'nin her Ajax yöntemi zaten bir "ertelenmiş nesne" (aslında ertelenmiş bir nesnenin sözünü) verir;

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

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

Yan not: sözler gotchas

Sözlerin ve ertelenmiş nesnelerin sadece konteynerler Gelecek bir değer için, değerin kendisi değildir. Örneğin, aşağıdakileri yaptığınızı 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 uyumsuzluk 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ı hemen bir jQuery Ajax Deferred nesnesi döndürür. Bu demektir ki if ifadesi her zaman bu Deferred nesnesini alacaktır. trueve kullanıcı giriş yapmış gibi devam edin. İyi değil.

Ama düzeltme çok kolay:

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 edilmez: Senkronize "Ajax" çağrılar

Bahsettiğim gibi, bazı (!) Asenkron işlemlerin eşzamanlı eşdeğerleri var. Kullanımlarını savunmuyorum, ama tamlık için, senkronize bir çağrıyı nasıl yapacağınız şöyle:

JQuery olmadan

Doğrudan kullanıyorsanız XMLHTTPRequest nesne, geçmek false üçüncü argüman olarak .open.

jQuery

Eğer kullanırsan jQuery, ayarlayabilirsiniz async seçeneği false. Bu seçeneğin olduğunu unutmayın. kullanımdan kaldırıldı jQuery 1.8'den beri. Daha sonra hala kullanabilirsiniz success geri arama veya erişim responseText mülkiyet jqXHR nesnesi:

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

Gibi başka bir jQuery Ajax yöntemini kullanırsanız $.get, $.getJSONvb. değiştirmek zorundasınız $.ajax(yalnızca yapılandırma parametrelerini $.ajax).

Dikkat et! Senkronize yapmak mümkün değildir JSONP istek. JSONP, doğası gereği her zaman asenkrondir (bu seçeneği bile düşünmemek için bir sebep daha).


4666
2018-01-08 17:06



@Pommy: jQuery'yi kullanmak isterseniz, onu eklemelisiniz. Bakınız docs.jquery.com/Tutorials:Getting_Started_with_jQuery. - Felix Kling
Çözüm 1'de, alt jQuery, bu satırı anlayamadım: If you use any other jQuery AJAX method, such as $.get, $.getJSON, etc., you have them to $.ajax. (Evet, bu durumda nickimin biraz ironik olduğunu anlıyorum) - gibberish
@gibberish: Mmmh, nasıl daha net hale getirileceğini bilmiyorum. Nasıl görüyorsun foo çağrılır ve ona bir işlev iletilir (foo(function(result) {....});)? result Bu işlev içinde kullanılır ve Ajax isteğinin yanıtıdır. Bu işleve başvurmak için foo'nun ilk parametresi çağrılır. callback ve atandı success anonim bir işlev yerine. Yani, $.ajax Arayacağım callback istek başarılı olduğunda. Biraz daha açıklamaya çalıştım. - Felix Kling
Bu sorunun cevabı ölmüştür, bu yüzden özetlenen değişiklikleri önermek için emin değilim, ancak şunu öneriyorum: 1) Eşzamanlı parçayı, nasıl yapılacağını gösteren bir kod olmadan neden kötü olduğunu basit bir tartışmaya dönüştürün. 2) Sadece daha esnek Ertelenmiş yaklaşımı göstermek için geri çağırma örneklerini kaldırın / birleştirin, ki bu da Javascript öğrenenler için takip etmenin biraz daha kolay olabileceğini düşünüyorum. - Chris Moschini
@Jessi: Sanırım cevabın bir kısmını yanlış anladın. Kullanamazsın $.getJSON Ajax isteğinin senkronize olmasını istiyorsanız. Ancak, etkinliğin isteğinin senkronize olmasını istememeniz gerekir, bu nedenle geçerli olmaz. Yanıtın daha önce açıklandığı gibi, cevabın üstesinden gelmek için geri aramalar veya sözler kullanmalısınız. - Felix Kling


Eğer öyleysen değil kodunuzda jQuery kullanarak, bu cevap tam size göre

Kodunuz şu satırlarda bir şey olmalı:

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ş yaptı, ben olmayan insanlar için bir alternatif sunmaya karar verdim.

(Yeni olanlar için not fetch API, Açısal veya vaatler Aşağıda bir başka cevap daha ekledim)


Ne bakıyorsun?

Bu, diğer sorudan "Sorunun açıklanması" nın kısa bir özeti, bunu okuduktan emin değilseniz, bunu okuyun.

bir AJAX için duruyor eşzamanlı olmayan. Bu, talebi göndermek (veya yanıtı almak) normal yürütme akışından çıkarılmak demektir. Örneğinde, .send hemen döner ve bir sonraki ifade, return result;olarak geçtiğiniz işlevden önce yürütülür success geri arama bile çağrıldı.

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

(Keman)

Değeri a geri döndü undefined Beri a=5 bölüm henüz idam edilmedi. AJAX böyle davranır; sunucu, tarayıcınıza bu değerin ne olduğunu bildirme şansını elde etmeden önce değeri döndürürsünüz.

Bu soruna olası bir çözüm kodlamaktır yeniden aktif , programınızı tamamladığınızda ne yapmanız gerektiğini söyler.

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

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

Buna denir CPS. Temel olarak, biz geçiyoruz getFive Tamamlandığında gerçekleştirilecek bir işlem, bir etkinlik tamamlandığında (AJAX çağrımız veya bu durumda zaman aşımı gibi) kodumuzun nasıl tepki vereceğini anlatırız.

Kullanım olurdu:

getFive(onComplete);

Hangi ekran "5" uyardı. (Keman).

Muhtemel çözümler

Bunu çözmek için temel olarak iki yol vardır:

  1. AJAX çağrısını senkronize edin (SJAX olarak adlandırın).
  2. Geri aramalarla doğru şekilde çalışmak için kodunuzu yeniden yapılandırın.

1. Eşzamanlı AJAX - Yapma !!

Senkronize AJAX gelince, yapma! Felix'in cevabı, bunun neden kötü bir fikir olduğuna dair bazı iddialı argümanlar getiriyor. Özetlemek gerekirse, sunucu yanıtı geri döndürene ve çok kötü bir kullanıcı deneyimi yaratana kadar kullanıcının tarayıcısını donduracaktır. İşte MDN'den alınan bir başka kısa özet: neden:

XMLHttpRequest, senkron ve asenkron iletişimin her ikisini de destekler. Bununla birlikte, genel olarak, performans nedenleriyle eşzamanlı taleplere asenkron istekleri tercih edilmelidir.

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

Eğer sen var Bunu yapmak için bir bayrak geçebilirsin: İşte nasıl:

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

Fonksiyonunuzun bir geri aramayı kabul etmesine izin verin. Örnek kodda foo bir geri dönüşü kabul etmek için yapılabilir. Kodumuzu nasıl anlatacağız tepki ne zaman foo tamamlar.

Yani:

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

Oluyor:

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

Burada anonim bir işleve geçtik, ancak mevcut bir işleve bir referansı kolayca aktarabiliriz ve şöyle görünür:

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

Bu tür geri arama tasarımının nasıl yapıldığı hakkında daha fazla bilgi için Felix'in cevabını kontrol edin.

Şimdi buna göre davranmak için foo'yu 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)

Artık foo fonksiyonumuzun, AJAX başarıyla tamamlandığında çalışacak bir eylemi kabul etmesini sağladık, buna ek olarak, yanıt durumunun 200 olup olmadığını kontrol ederek ve buna göre hareket ederek (başarısız bir işleyici ve benzeri) bunu genişletebiliriz. Sorunumuzu etkili bir şekilde çözüyoruz.

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


888
2018-05-29 23:30



"senkronize istekler kodun yürütülmesini engeller ve bellek ve olayları sızdırabilir" senkronize bir istek nasıl bellek sızdırabilir? - Matthew G
@MatthewG Üzerinde bir ödül ekledim bu soru, Ben neleri dışarı atabileceğimi göreceğim. Alıntıyı, cevabın ortalama zamanından kaldırıyorum. - Benjamin Gruenbaum
Sadece referans için, XHR 2 bize onload yalnızca işleyen readyState olduğu 4. Tabii ki, IE8'de desteklenmiyor. (iirc, onay gerekebilir.) - Florian Margaine
Anonim bir işlevi geri arama olarak nasıl ileteceğinize dair açıklamanız geçerli ancak yanıltıcıdır. Örnek var bar = foo (); bir değişkenin tanımlanmasını isterken, önerilen foo'nuz (functim () {}); çubuk tanımlanmaz - Robbie Averill
@BenjaminGruenbaum İçeriği nasıl iade ederim? resulthtml ile basmak yerine kodumda başka bir yerde kullanabileceğim bir değişkede? - MorganFR


XMLHttpRequest 2 (ilk olarak Benjamin Gruenbaum & Felix Kling'in cevaplarını okuyun)

JQuery kullanmıyor ve modern tarayıcılarda ve ayrıca mobil tarayıcılarda çalışan güzel bir XMLHttpRequest 2 istiyorsanız, bunu şu şekilde kullanmanızı öneriyorum:

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 ayarlanmıştır (bu nedenle fazladan gereksiz kapanma olmaz).
  3. Yeni yükü kullanır (böylece readystate && durumunu kontrol etmek zorunda kalmazsınız)
  4. XMLHttpRequest 1 sinir bozucu hale getiren hatırlamıyorum bazı diğer durumlar vardır.

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

En basit:

this.response

Ya da nedense bind() bir sınıfa geri dönüş:

e.target.response

Örnek:

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

Veya (yukarıdaki anonim işlevler her zaman bir problemdir):

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

Daha kolay bir şey değil.

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

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

Tüm modern tarayıcılarda desteklenmiştir. Ve XMLHttpRequest 2 var olduğundan bu yaklaşımı kullandığımı onaylayabilirim. Kullandığım tüm tarayıcılarda hiç sorun yaşamadım.

onreadystatechange, yalnızca üstbilgileri 2. aşamada almak istiyorsanız kullanışlıdır.

Kullanmak XMLHttpRequest Değişken adı, geri çağırma işlemini kaybettiğiniz onload / oreadystatechange kapatmalarının içinde yürütmeniz gerektiğinden, başka büyük bir hatadır.


Ş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, ama almak ve göndermek.

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

Bunu söylediğimde ... neden bunu kolay yoldan yapmıyorsun?


Yorumda belirtildiği gibi, hata ve senkronizasyon kullanımı, cevabın noktasını tamamen bozar. Ajax'ı uygun şekilde kullanmanın güzel bir kısa yolu hangisi?

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

Ama gerçekten bir hata çıkarmak için bir tek Her tarayıcı bir hata attığında yanlış bir URL yazmaktır.

Özel işleyicileri ayarlarsanız, message dizisini blob dizisi arabelleğine veya herhangi bir şeye ayarlarsanız hata işleyicileri yararlı olabilir.

'POSTAPAPAP' yöntemini yöntem olarak iletseniz bile bir hata atmaz.

Formdata olarak 'fdggdgilfdghfldj' iletseniz bile hata alamaz.

İlk durumda hata içeride displayAjax() altında this.statusText gibi Method not Allowed.

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

etki alanları arası izin verilmez otomatik olarak hata atar.

Hata yanıtında hata kodu yok.

Sadece var this.type hataya ayarlanmış.

Hatalar üzerinde hiçbir kontrolünüz yoksa, neden bir hata işleyici ekleyelim? 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 kontrolüne gerek yoktur. ;)

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


Bir dosyayı senkronize oku

Bunu yapma.

Tarayıcıyı bir süre engellemek istiyorsanız, güzel bir büyük txt dosyasını senkronize edin.

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 senkronize olmayan bir şekilde yapmak için başka bir yolu yoktur. (Evet, setTimeout döngüsü ile ... ama cidden?)

Başka bir nokta da ... eğer API'larla çalışıyorsanız ya da sadece listenin dosyalarına sahipseniz veya her bir istek için her zaman farklı işlevler kullanıyorsanız ...

Sadece her zaman aynı XML / JSON'u yüklediğiniz veya yalnızca bir fonksiyona ihtiyacınız olan 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.

Eğer işlevi EXTEND 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, sadece GET ile ...

Ancak XMLHttpRequest 2 ile bir çok şey yapabilirsiniz:

Bir indirme yöneticisi (her iki tarafta da aralıkları kullanarak, filereader, dosya sistemi), çeşitli görüntü düzenleyicileri tuval kullanarak, base64images ile websql veritabanlarını doldurdukça ve çok daha fazlasını yaptım ... Ama bu durumlarda yalnızca bu amaçla bir işlev oluşturmalısınız. ... bazen bir blob, dizi arabelleklerine ihtiyacınız var, üstbilgileri ayarlayabilir, mimetype'i geçersiz kılabilir ve çok daha fazlası var ...

Ama buradaki soru, Ajax yanıtının nasıl iade edileceğidir ... (Kolay bir yol ekledim.)


304
2017-08-19 08:06



Bu cevap güzel olsa da (ve hepimiz Aşk XHR2 ve kayıt dosyası verileri ve multipart veri tamamen harika) - bu, XHR'yi JavaScript ile yayınlamak için sözdizimsel şeker gösterir - bunu bir blog gönderisine (isterim) ya da bir kütüphaneye koymak isteyebilirsiniz ( isim x, ajax veya xhr daha hoş olabilir :)). Bir AJAX çağrısından yanıtın nasıl geri döndüğünü ele almıyorum. (birisi hala yapabilirdi var res = x("url") ve neden işe yaramadığını anlamıyorum;)). Bir yan not - geri döndü eğer güzel olurdu c Bu yöntem sayesinde kullanıcılar error vb. - Benjamin Gruenbaum
2.ajax is meant to be async.. so NO var res=x('url').. Bu sorunun tam noktası ve cevaplar :) - Benjamin Gruenbaum
İlk satırda, sahip olduğunuz değerin üzerine yazıyorsanız, neden işlevlerde bir 'c' parametresi var? bir şey mi eksik? - Brian H.
Birden çok kez "var" yazmamak için parametreleri yer tutucu olarak kullanabilirsiniz. - cocco
@cocco Yani bir yanıltıcı, okunamaz kod yazdınız. Cevap Birkaç tuş vuruşunu kaydetmek için? Lütfen bunu yapma. - stone


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

Bu AngularJS, jQuery (ertelenmiş), yerel XHR'nin yerine (getirme), EmberJS, BackboneJS'nin kaydetme veya vaatleri veren herhangi bir düğüm kitaplığı anlamına gelir.

Kodunuz şu satırlarda bir şey olmalı:

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 yazarak iyi bir iş çıkardı. Yerel XHR için bir cevabım var. Bu cevap, ön uç veya arka uçtaki sözlerin genel kullanımı içindir.


Temel konu

Tarayıcıda ve NodeJS / io.js ile sunucuda Javascript eş zamanlılık modeli eşzamanlı olmayan ve tepkili.

Bir söz veren bir yöntemi çağırdığınızda, then işleyicileri her zaman Eşzamansız yürütülür - yani sonra onların altında olmayan bir kod .then işleyicisi.

Bu, geri döndüğünüzde anlamına gelir data  then tanımladığınız işleyici henüz çalışmadı. Bu, 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

Değeri data olduğu undefined Beri data = 5 bölüm henüz idam edilmedi. Muhtemelen bir saniye içinde yürütülür, ancak o zamana dönüş değeri ile ilgisi yoktur.

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

Bu soruna olası bir çözüm kodlamaktır yeniden aktif , programınızı tamamladığınızda ne yapmanız gerektiğini söyler. Vaatler, doğada zamansal (zamana duyarlı) olarak aktif olarak bunu etkinleştirir.

Vaatlerde hızlı tekrar

Bir sözdür zaman içindeki değer. Vaatler devlete sahip, beklemeden beklemede olarak başlarlar ve şu şekilde anlaşabilirler:

  • yerine Hesaplamanın başarıyla tamamlandığı anlamına gelir.
  • reddedilen hesaplamanın başarısız olduğu anlamına gelir.

Bir söz sadece devletleri değiştirebilir bir Zamanlar Bundan sonra her zaman aynı durumda kalacaktır. Ekleyebilirsiniz then İşleyicileri değerlerini ayıklamak ve hataları işlemek için söz verir. then işleyicileri izin zincirleme çağrıların Vaatler tarafından oluşturulur onları döndüren API'leri kullanarak. Örneğin, daha modern AJAX değişimi fetch veya jQuery's $.get iade sözleri.

Aradığımızda .then bir söz ve dönüş ondan bir şey - söz veriyoruz işlenen değer. Başka bir söz verirsek, harika şeyler alırız, ama atlarımızı tutalım.

Sözlerle

Yukarıdaki sorunu sözlerle nasıl çözebileceğimizi görelim. İlk olarak, yukarıda sözü verdiğimiz sözleri, Söz kurucu Gecikme fonksiyonu oluşturmak için:

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 sözler kullanmaya dönüştürdükten sonra, kullanabiliriz then saymak için:

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 değer eşzamanlılık modeli nedeniyle yapamadığımız - bir sargı yapabileceğimiz bir değer için paketini açmak ile then. Açabileceğin bir kutu gibi. then.

Bunu uygulamak

Bu, orijinal API aramanı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 sadece iyi çalışıyor. Zaten eşzamansız çağrılardan değer döndüremediğimizi öğrendik, ancak söz vermeyi kullanabiliriz ve işlemleri gerçekleştirmek için onları zincirleyebiliriz. Artık yanıtın eşzamansız bir çağrıdan nasıl döndürüleceğini biliyoruz.

ES2015 (ES6)

ES6 tanıttı jeneratörler Bunlar, ortada geri dönebilen ve daha sonra bulundukları noktaya dönebilen işlevlerdir. 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;
}

Döndüren bir işlev yineleyici dizi boyunca 1,2,3,3,3,3,.... hangi yinelenebilir. Bu kendi başına ilginç ve pek çok olasılık için yer açsa da, ilginç bir durum var.

Ürettiğimiz dizi, sayılardan ziyade bir eylem dizisi ise - bir eylem yapıldığında işlevi durdurabilir ve işlevi yeniden başlatmadan önce bekleyebiliriz. Yani bir sayı dizisi yerine, bir dizi diziye ihtiyacımız var. gelecek değerler - yani: vaatler.

Bu biraz zor ama çok güçlü bir hile, senkronize olmayan bir şekilde eşzamanlı kod yazmamıza izin veriyor. Bunu sizin için yapan birkaç "koşucu" var, bir tane kısa kod satırı yazıyor ama bu cevabın kapsamı dışında. Bluebird kullanıyorum Promise.coroutine burada, ama diğer sarmalayıcılar gibi co veya 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 koroutinlerden tüketebileceğimiz bir söz verir. Ö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 yapabilirsiniz await söz vermek. Bu, yukarıdaki ES6 önerisi için yalnızca "şeker" (güzel sözdizimi) ekleyerek async ve await anahtar kelimeler. 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 sadece bir söz verir.


245
2018-05-12 02:22



ES 2016, bu soruna bir kez ve herkes için nihai çözüm gibi görünüyor. - ShrekOverflow
Bir kez ve herkes için kesinlikle nihai çözüm? Tamamen kesin bir çözümden ne haber, ne de bir artı için? - Shmiddty
nerden "beş" aldın? asla hiçbir yerde beyan edilmez. - recurf
@recurf her şeyden önce "bu cehennem" bu site için pek uygun değildir (çocuklar bir tane için de kullanıyor). İkincisi - bu bir işlev parametresi, başka bir yerde adlandırılmış olabilirdi ve o kadar iyi çalışırdı. - Benjamin Gruenbaum
Soruyu cevapladın mı? Yanıtı eşzamansız bir çağrıdan nasıl geri döndürebilirim? @BenjaminGruenbaum - OnPrinciples


Ajax'ı yanlış kullanıyorsunuz. Bu fikir bir şey döndürmek değil, bunun yerine verileri işleyen bir geri çağırma işlevi olarak adlandırılan bir veriye vermektir.

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önderici işleyicisindeki herhangi bir şey geri dönüş hiçbir şey yapmayacaktır. Bunun yerine ya veriyi elden geçirmelisiniz ya da onunla ne istediğinizi doğrudan başarı fonksiyonunda yapmalısınız.


193
2018-05-23 02:05



Bu cevap tamamen anlamlıdır ... başarı yönteminiz sadece geri aramada bir geri aramadır. Sadece sahip olabilirsin success: handleData ve işe yarayacaktı. - Jacques
Ve ne "handleData" dışında "responseData" dönmek isterseniz ... :) nasıl yapacaksınız ...? ... basit bir geri dönüşün, ajax'in "başarılı" geri dönüşüne ... ... ve "handleData" dışında ... - pesho hristov
@Jacques & @pesho hristov Bu noktayı kaçırdınız. Gönderen işleyici değil success yöntem, bu çevre kapsamı $.ajax. - travnik
@travnik Bunu özlemedim. HandleData içeriğini alıp başarı yöntemine koyduysanız, tam olarak aynı davranacaktır. - Jacques


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

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

185
2018-02-18 18:58



Kimin olumsuz oy kullandığını bilmiyorum. Ancak bu, etrafta çalışan bir çalışmadır, aslında bu yaklaşımı bütün bir uygulama oluşturmak için kullandım. Jquery.ajax, yukarıdaki yaklaşımı kullanmak için verileri geri döndürmez. Eğer yanlışsa, lütfen açıklayınız ve bunu yapmanın daha iyi bir yolunu öneriniz. - Hemant Bavle
Üzgünüm, bir yorum bırakmayı unuttum (genellikle yaparım!). Onu reddettim. İndirgemelerin fiili doğruluğunu veya eksikliğini göstermedikleri, bağlamdaki yararlılığı ya da eksikliğini gösterdiler. Cevabınızı faydalı buluyorum çünkü Felix'i daha önce sadece daha ayrıntılı olarak açıklıyor. Bir yan notta, neden JSON ise cevabı dizginlendirirsiniz? - Benjamin Gruenbaum
Tamam .. @Benjamin stringale, JSON Nesnesini string'e dönüştürmek için kullandım. Ve amacını açıkladığın için teşekkürler. Daha ayrıntılı cevaplar vermeyi akılda tutacak. - Hemant Bavle
Ve "responseObj" 'i "successCallback" dışında döndürmek isterseniz ... ... nasıl yapacaksınız ...? ... basit bir geri dönüşün, ajax'in "başarılı" geri dönüşüne ... ... geri dönüşü ve "başarıyakalan" dışında ... - pesho hristov


Korkunç görünümlü, elle çizilmiş bir çizgi ile cevap vereceğim. İkinci görüntü neden nedeni result olduğu undefined kod örneğinizde.

enter image description here


155
2017-08-11 14:17



Bir resim bin kelime değerinde bir olup, Kişi A - Aracını düzeltmek için kişinin B detaylarını sorun, Kişi B - Ajax Call yapar ve yanıt alındığında, Ajax Success fonksiyonu, B Personeli fonksiyonunu çağırır ve cevabı ona argüman olarak iletir, A yanıtı aldığında, sunucudan yanıtı bekler. - stom
Kavramları göstermek için her resimdeki kod satırlarını eklediyseniz harika olur. - Hassan Baig
Karşılaştığım en iyi açıklama - anonymous


Angular1

Kullanan kişiler için angularjs, kullanarak bu durumu halledebilir Promises.

İşte diyor ki,

Vaatler, asenkron olmayan işlevler için kullanılabilir ve birden çok işlevi birbirine zincirleme olanağı sağlar.

Güzel bir açıklama bulabilirsiniz İşte Ayrıca.

Örnekte bulundu docs aşağıda belirtilen.

  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

İçinde Angular2 Aşağıdaki örneğe bakın, ancak Tavsiye edilen kullanmak Observables ile 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));
}

Bakın orijinal buraya gönderin. Ama Typescript desteklemiyor native es6 VaatlerBunu kullanmak isterseniz, bunun için eklentiye ihtiyacınız olabilir.

Ek olarak burada sözler spec Burada tanımlayın.


114
2017-08-26 08:11



Bu, sözlerin bu sorunu nasıl çözeceğini açıklamıyor. - Benjamin Gruenbaum
Çalışmıyor. promiseB 'undefined' alacak - An Overflowed Stack
jQuery ve getirmek yöntemlerin her ikisi de vaatler verir. Cevabınızı gözden geçirmenizi öneririm. JQuery'nin tamamen aynı olmamasına rağmen (o zaman orada, ancak yakalamak değil). - Tracker1


Buradaki cevapların çoğu, tek bir asenkronizasyon işleminiz olduğunda yararlı önerilerde bulunur, ancak bazen bu, zaman uyumsuz bir işlem yapmanız gerektiğinde ortaya çıkar. her bir dizi veya diğer liste benzeri bir yapıya giriş. Günaha 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;
}

Çalışmamanın nedeni, geri aramaların doSomethingAsync Sonuçları kullanmaya çalıştığınız zaman henüz başlamadım.

Yani, bir diziniz varsa (veya bir çeşit listeniz varsa) ve her bir giriş için asenkron işlemleri yapmak istiyorsanız, iki seçeneğiniz vardır: İşlemleri paralel olarak (örtüşen) veya seri olarak (sırayla birbiri ardına) yapın.

Paralel

Hepsini başlatabilir ve kaç tane geri arama beklediğinizi takip edebilir ve daha sonra bu geri dönüşleri 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;
}

(Biz ile giderdik expecting ve sadece kullan results.length === theArray.lengthama bu bizi açıklığa kavuşturuyor. theArray aramalar olağanüstü iken değiştirilir ...)

Nasıl kullandığımızı dikkat edin index itibaren forEach sonucu kaydetmek için results Sonuçlar, emir yerine gelse bile, giriş ile aynı pozisyondadır (asenkron çağrılar başlatıldıkları sırayla tamamlanmadıklarından).

Ama ya ihtiyacın varsa dönüş Bu bir işlevden sonuçlanır? Diğer cevaplar işaret ettiği gibi, yapamazsınız; işlevinizi kabul etmeniz ve bir geri arama çağırmanız gerekir (ya da Söz vermek). İş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;
}

Ya da bir geri dönen bir sürüm Promise yerine:

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

Tabi eğer doSomethingAsync hatalardan geçti, biz kullanırdık reject Bir hata aldığımızda sözü reddetmek için.)

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

(Veya dönüşümlü olarak, doSomethingAsync Bu bir söz verir ve ardından aşağıdakileri yapın ...)

Eğer doSomethingAsync sana bir verir Söz vermek, kullanabilirsiniz Promise.all:

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

Örnek:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry, function(result) {
            results.push(result);
        });
    }));
}
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;
}

Bunu not et Promise.all tüm kararları verildiğinde verdiğiniz tüm sözlerin bir sonucu ile sözünü çözer veya söz verdiğinde sözünü reddeder. ilk verdiğiniz sözlerin reddedilmesi.

Dizi

İşlemlerin paralel olmasını istemediğini varsayalım? Onları birbiri ardına çalıştırmak istiyorsanız, sonraki işlemlere başlamadan önce her işlemin tamamlanmasını beklemeniz gerekir. İşte bunu yapan ve sonuca bir geri çağırma yapan 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);
});

(Seri olarak işi yaptığımız için sadece kullanabiliriz results.push(result) bildiğimiz için sonuçtan çıkmayacağız. Yukarıda biz kullanabilirdik results[index] = result;ancak aşağıdaki örneklerin bazılarında kullanmak için bir dizinimiz 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, için bir sarıcı oluşturmak doSomethingAsync Bu size bir söz verir ve aşağıdakileri yapın ...)

Eğer doSomethingAsync Eğer ES2017 + sözdizimini kullanabiliyorsanız (örneğin bir transpiler ile) Babil), bir async fonksiyon ile for-of ve 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 (henüz) kullanamıyorsanız, bir varyasyon kullanabilirsiniz. "Söz indir" desen (Bu her zamanki Promise azaltılmasından daha karmaşıktır çünkü sonucu bir sonuçtan diğerine aktarmıyoruz, ancak sonuçlarını bir dizide toplamak yerine):

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

... hangisi daha az hantal ES2015 + ok fonksiyonları:

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


91
2018-05-03 16:59



Nasıl açıklar mısınız if (--expecting === 0) Kodun bir kısmı lütfen çalışıyor mu? Çözümünüzün geri çağırma sürümü benim için harika çalışıyor, ben sadece, bu ifadeyle, tamamlanan yanıtların sayısını nasıl kontrol ettiğinizi anlamıyorum. Benim için sadece bilgi eksikliği olduğunu takdir ediyorum. Çek yazmanın alternatif bir yolu var mı? - Sarah
@Sarah: expecting değeri ile başlar array.lengthNe kadar talepte bulunacağız. Tüm bu isteklerin başlatılmasına kadar geri aranmayacaklarını biliyoruz. Geri aramada if (--expecting === 0) Bunu yapar: 1. Azalır expecting (Bir yanıt aldık, bu nedenle bir daha az yanıt bekliyoruz) ve değer sonra Azalma 0 (daha fazla cevap beklemiyoruz), işimiz bitti! - T.J. Crowder