Bir sözden birden fazla değeri nasıl doğru bir şekilde döndürürsünüz?


87

Son zamanlarda birkaç kez, nasıl çözeceğimi bilmediğim belirli bir durumla karşılaştım. Aşağıdaki kodu varsayalım:

somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( amazingData ) {
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
}

Ben erişmesini istediğiniz nerede Şimdi bir durum ortaya çıkabilecek amazingDataiçinde afterSomethingElse.

Açık bir çözüm, bir dizi veya hash döndürmektir afterSomething, çünkü bir işlevden yalnızca bir değer döndürebilirsiniz. Ama afterSomethingElse2 parametreyi kabul etmenin ve onu aynı şekilde çağırmanın bir yolu olup olmadığını merak ediyorum , çünkü bunu belgelemek ve anlamak çok daha kolay görünüyor.

İstediğime Q.spreadbenzer bir şey yapan var olduğu için sadece bu olasılığı merak ediyorum.




ES6'daki Atamanın yeniden yapılandırılması yardımcı olacaktır. burayı
Ravi Teja

Yanıtlar:


88

Tıpkı bir işlevden birden çok değer döndüremediğiniz gibi, birden çok özelliğe sahip bir sözü de çözemezsiniz . Bir söz, kavramsal olarak zaman içindeki bir değeri temsil eder, bu nedenle bileşik değerleri temsil ederken, bir sözde birden fazla değer koyamazsınız.

Bir vaat, doğası gereği tek bir değerle çözülür - bu, Q'nun nasıl çalıştığının, Promises / A + spesifikasyonunun nasıl çalıştığının ve soyutlamanın nasıl çalıştığının bir parçasıdır .

Elde edebileceğiniz en yakın şey, Q.spreaddizileri kullanmak ve döndürmek veya destekleniyorsa veya BabelJS gibi bir kopyalama aracı kullanmak istiyorsanız ES6 imhasını kullanmaktır.

Bağlamın bir vaat zincirinden aşağı aktarılmasına gelince, lütfen Bergi'nin bu konudaki mükemmel kurallarına bakın .


16
Birden çok özelliği olan bir nesneyi çözmenin nesi yanlış? Bir çözümden birden fazla değer elde etmenin basit bir yolu gibi görünüyor.
jfriend00

6
Bunu yapmak çok iyi
Benjamin Gruenbaum

.spread()Bu ilgili yanıtta Bluebird'ün gösterilmesi için Promise'ı da uzatabilirsiniz : stackoverflow.com/a/22776850/1624862
Kevin Ghadyani

Promise.all () davranışı bununla çelişiyor gibi görünüyor. Promise.all([a, b, c]).then(function(x, y, z) {...})a, b ve c'nin çözümlenmiş değerlerini değerlendiren x, y ve z ile tüm modern Javascript motorlarında düzgün çalışır. Bu nedenle, dilin bunu kullanıcı kodundan kolayca (veya mantıklı bir şekilde) yapmanıza izin vermediğini söylemek daha doğrudur (çünkü bir Promise'i doğrudan bir then cümlesinden döndürebilirsiniz, değerlerinizi sözlere sarabilir ve sonra bunları Promise ile sarabilirsiniz. .all (), kıvrımlı bir şekilde de olsa istenen davranışı elde etmek için).
Austin Hemmelgarn

3
, Bu sadece yanlış @AustinHemmelgarn Promise.allyerine getiren bir dizi . In Promise.all([a,b]).then((a, b) => bolduğunu undefined. İşte bu yüzden .then(([a, b]) =>yıkıcı bir ödev olanı yapmanız gerekiyor
Benjamin Gruenbaum

39

yalnızca bir değer iletebilirsiniz, ancak bu, içinde birden çok değer bulunan bir dizi olabilir, örneğin:

function step1(){
  let server = "myserver.com";
  let data = "so much data, very impresive";
  return Promise.resolve([server, data]);
}

diğer tarafta, tek tek değerleri elde etmek için ES2015 için yıkıcı ifadeyi kullanabilirsiniz .

function step2([server, data]){
  console.log(server); // print "myserver.com"
  console.log(data);   // print "so much data, very impresive"
  return Promise.resolve("done");
}

her ikisini de söz olarak adlandırmak

step1()
.then(step2)
.then((msg)=>{
  console.log(msg); // print "done"
})

5
Buna "yapısökümcü" değil, yıkıcı deniyor ve bu bir operatör değil: - /
Bergi

3
Btw, yıkımı doğrudan parametrelerde kullanabilirsiniz: function step2([server, data]) { …- bu şekilde örtük globallere atamaktan da kaçınırsınız. Ve gerçekten kullanmalısınız returnveya Promise.resolvedeğil, new Promisesizin örneklerde yapıcı.
Bergi

Teşekkürler @Bergi öneriler için!
Alejandro Silva

20

Her iki değeri de içeren bir nesneyi döndürebilirsiniz - bunda yanlış bir şey yok.

Diğer bir strateji, değeri geçmek yerine kapanışlar yoluyla korumaktır :

somethingAsync().then(afterSomething);

function afterSomething(amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

Kısmen satır içi yerine tamamen (eşdeğer, muhtemelen daha tutarlı):

somethingAsync().then(function (amazingData) {
  return processAsync(amazingData).then(function (processedData) {
    // both amazingData and processedData are in scope here
  });
}

3
Bir thenbaşkasının içine dönmek sorun değil thenmi? Bu bir anti-model değil mi?
robe007

@ robe007'nin söylediği gibi, bu 'callback cehenneme' benzemez mi? burada yuvalama işleminiz geri arama işlevleri yerine engelliyor; bu, vaatler alma amacını ortadan
kaldırır

5

Yapabileceğiniz iki şey, bir nesneyi iade etmek

somethingAsync()
    .then( afterSomething )
    .then( afterSomethingElse );

function processAsync (amazingData) {
     //processSomething
     return {
         amazingData: amazingData, 
         processedData: processedData
     };
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}

function afterSomethingElse( dataObj ) {
    let amazingData = dataObj.amazingData,
        processedData = dataObj.proccessedData;
}

Kapsamı kullanın!

var amazingData;
somethingAsync()
  .then( afterSomething )
  .then( afterSomethingElse )

function afterSomething( returnedAmazingData ) {
  amazingData = returnedAmazingData;
  return processAsync( amazingData );
}
function afterSomethingElse( processedData ) {
  //use amazingData here
}

3

İşte senin yapman gerektiğini düşündüğüm şekilde.

zinciri bölmek

Her iki işlev de amazingData kullanacağından , onlara ayrılmış bir işlevde sahip olmak mantıklıdır. Bunu genellikle bazı verileri yeniden kullanmak istediğimde yapıyorum, bu nedenle her zaman bir işlev arg olarak mevcut.

Örneğiniz bazı kodları çalıştırdığı için, hepsinin bir işlev içinde açıklandığını varsayacağım. Bunu toto () olarak arayacağım . Sonra ikimiz de çalışacak başka bir işlevi olacaktır afterSomething () ve afterSomethingElse () .

function toto() {
    return somethingAsync()
        .then( tata );
}

Ayrıca , genellikle Promises ile gitmenin yolu olduğu için bir dönüş ifadesi eklediğimi fark edeceksiniz - her zaman bir söz verirsiniz, böylece gerekirse zincirlemeye devam edebiliriz. Burada somethingAsync () üretecek amazingData ve yeni işlev içinde her yerde mevcut olacaktır.

Şimdi bu yeni işlevin neye benzeyeceği tipik olarak processAsync () 'in de zaman uyumsuz olup olmadığına bağlıdır ?

processAsync eşzamansız değil

ProcessAsync () eşzamansız değilse, işleri aşırı karmaşık hale getirmek için bir neden yok . Bazı eski iyi sıralı kodlar bunu yapabilirdi.

function tata( amazingData ) {
    var processed = afterSomething( amazingData );
    return afterSomethingElse( amazingData, processed );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( amazingData, processedData ) {
}

AfterSomethingElse () işlevinin eşzamansız bir şey yapıp yapmadığının önemli olmadığını unutmayın . Aksi takdirde bir söz iade edilir ve zincir devam edebilir. Değilse, sonuç değeri döndürülür. Ancak işlev a then () ' dan çağrıldığı için , değer yine de bir söze sarılacaktır (en azından ham Javascript'te).

processAsync asenkron

Eğer processAsync () eşzamansızsa, kod biraz farklı görünecektir. Burada afterSomething () ve afterSomethingElse () ' nin başka hiçbir yerde yeniden kullanılmayacağını düşünüyoruz.

function tata( amazingData ) {
    return afterSomething()
        .then( afterSomethingElse );

    function afterSomething( /* no args */ ) {
        return processAsync( amazingData );
    }
    function afterSomethingElse( processedData ) {
        /* amazingData can be accessed here */
    }
}

AfterSomethingElse () için öncekiyle aynı . Asenkron olabilir veya olmayabilir. Bir söz iade edilecek veya bir değer çözülmüş bir söze sarılacaktır.


Kodlama tarzınız yapmakta olduğum şeye oldukça yakın, bu yüzden 2 yıl sonra bile cevap verdim. Her yerde anonim işlevlere sahip olmanın büyük bir hayranı değilim. Okumakta zorlanıyorum. Toplumda oldukça yaygın olsa bile. Biz yerini gibi öyle geri arama-cehennem bir tarafından söz-araf .

Ayrıca işlevleri adını tutmak ister sonra kısa. Zaten sadece yerel olarak tanımlanacaklar. Ve çoğu zaman işi yapmak için başka bir yerde tanımlanmış - çok yeniden kullanılabilir - başka bir işlevi çağırırlar. Bunu sadece 1 parametresi olan fonksiyonlar için bile yapıyorum, bu nedenle fonksiyon imzasına bir parametre ekleyip çıkardığımda fonksiyonu içeri ve dışarı almam gerekmiyor.

Örnek yemek

İşte bir örnek:

function goingThroughTheEatingProcess(plenty, of, args, to, match, real, life) {
    return iAmAsync()
        .then(chew)
        .then(swallow);

        function chew(result) {
            return carefullyChewThis(plenty, of, args, "water", "piece of tooth", result);
        }

        function swallow(wine) {
            return nowIsTimeToSwallow(match, real, life, wine);
        }
}

function iAmAsync() {
    return Promise.resolve("mooooore");
}

function carefullyChewThis(plenty, of, args, and, some, more) {
    return true;
}

function nowIsTimeToSwallow(match, real, life, bobool) {
}

Promise.resolve () öğesine çok fazla odaklanmayın . Kararlı bir söz oluşturmanın hızlı bir yoludur. Bununla başarmaya çalıştığım şey, çalıştırdığım tüm koda tek bir yerde sahip olmaktır - hemen altında . Daha açıklayıcı bir ada sahip diğer tüm işlevler yeniden kullanılabilir.

Bu tekniğin dezavantajı, birçok işlevi tanımlamasıdır. Ancak korktuğum her yerde anonim işlevlerden kaçınmak için gerekli bir acı. Ve yine de risk nedir: yığın taşması mı? (şaka!)


Diğer yanıtlarda tanımlandığı gibi dizileri veya nesneleri kullanmak da işe yarayacaktır. Bu bir bakıma Kevin Reid tarafından önerilen cevaptır .

Ayrıca bind () veya Promise.all () kullanabilirsiniz. . Yine de kodunuzu bölmenizi gerektireceklerini unutmayın.

bağlama kullanarak

Eğer fonksiyonları yeniden kullanılabilir ama gerçekten içeride ne olduğunu tutmak gerekmez tutmak istiyorsanız o zaman çok kısa, kullanabileceğiniz bağlama () .

function tata( amazingData ) {
    return afterSomething( amazingData )
        .then( afterSomethingElse.bind(null, amazingData) );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( amazingData, processedData ) {
}

Basit tutmak için, bind () çağrıldığında işlevin başına argümanlar listesini (ilki hariç) ekler.

Promise.all kullanarak

Gönderinizde spread () kullanımından bahsettiniz . Kullandığınız çerçeveyi hiç kullanmadım, ama işte onu nasıl kullanabilmeniz gerektiği.

Bazıları Promise.all () 'ın tüm sorunların çözümü olduğuna inanıyor , bu yüzden sanırım bahsedilmeyi hak ediyor.

function tata( amazingData ) {
    return Promise.all( [ amazingData, afterSomething( amazingData ) ] )
        .then( afterSomethingElse );
}

function afterSomething( amazingData ) {
    return processAsync( amazingData );
}
function afterSomethingElse( args ) {
    var amazingData = args[0];
    var processedData = args[1];
}

Verileri Promise.all () ' a aktarabilirsiniz - dizinin varlığına dikkat edin - söz verdiği sürece, ancak hiçbirinin başarısız olduğundan emin olun, aksi takdirde işlem duracaktır.

Ve args bağımsız değişkeninden yeni değişkenler tanımlamak yerine , harika işler için then () yerine spread () kullanabilmelisiniz .


3

Basitçe bir nesne oluşturun ve bu nesneden argümanlar çıkarın.

let checkIfNumbersAddToTen = function (a, b) {
return new Promise(function (resolve, reject) {
 let c = parseInt(a)+parseInt(b);
 let promiseResolution = {
     c:c,
     d : c+c,
     x : 'RandomString'
 };
 if(c===10){
     resolve(promiseResolution);
 }else {
     reject('Not 10');
 }
});
};

PromiseResolution'dan argümanlar çekin.

checkIfNumbersAddToTen(5,5).then(function (arguments) {
console.log('c:'+arguments.c);
console.log('d:'+arguments.d);
console.log('x:'+arguments.x);
},function (failure) {
console.log(failure);
});

2

Bir sözden ne iade ederseniz edin, bir sonrakinde açılacak bir söze sarılacaktır. .then() aşamada .

Aşağıdakiler gibi bir veya daha fazla eşzamanlı değerin yanında bir veya daha fazla sözü geri vermeniz gerektiğinde ilginç hale gelir;

Promise.resolve([Promise.resolve(1), Promise.resolve(2), 3, 4])
       .then(([p1,p2,n1,n2]) => /* p1 and p2 are still promises */);

Bu gibi durumlarda , sonraki aşamada paketlenmemiş vaatler Promise.all()almak p1ve p2sözler almak için kullanmak çok önemlidir.then()

Promise.resolve(Promise.all([Promise.resolve(1), Promise.resolve(2), 3, 4]))
       .then(([p1,p2,n1,n2]) => /* p1 is 1, p2 is 2, n1 is 3 and n2 is 4 */);

1

Rxjs ile temsil edilen Gözlemlenebilir'i kontrol edebilirsiniz , birden fazla değer döndürmenizi sağlar .


0

Basitçe bir demet döndür:

async add(dto: TDto): Promise<TDto> {
console.log(`${this.storeName}.add(${dto})`);
return firebase.firestore().collection(this.dtoName)
  .withConverter<TDto>(this.converter)
  .add(dto)
  .then(d => [d.update(this.id, d.id), d.id] as [any, string])
  .then(x => this.get(x[1]));

}

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.