Bir nesnenin bir söz olup olmadığını nasıl anlarım?


336

İster ES6 Sözü, ister mavi kuş Sözü, Q Sözü vb.

Belirli bir nesnenin bir Söz olup olmadığını nasıl test edebilirim?


3
En iyi ihtimalle bir .thenyöntem olup olmadığını kontrol edebilirsiniz , ancak bu size sahip olduğunuz şeyin kesin bir Promise olduğunu söylemez. Bu noktada bileceğiniz tek şey .then, bir Söz gibi bir yöntemi ortaya çıkaran bir şeye sahip olmanızdır .
Scott Offen

@ScottOffen spesifikasyonunu açıkça bir ayrım yapmaz.
Benjamin Gruenbaum

6
Demek istediğim, herkesin .thenbir Vaat olmayan bir yöntemi ortaya koyan , bir Vaat gibi davranmayan ve bir Vaat gibi kullanılma niyeti olmayan bir nesne yaratabileceğidir . Bir .thenyöntemi kontrol etmek size if nesnesinin bir yöntemi olmadığını.then , o zaman bir Sözün olmadığını söyler . Ters - Bir varlığı bu .theno yöntem vasıtası yapmak bir Promise var - her zaman doğru olmayabilir.
Scott Offen

3
@ScottOffen Tanım olarak, bir vaadi tanımlamanın tek yerleşik yolu, onun bir .thenyöntemi olup olmadığını kontrol etmektir . Evet, bu yanlış pozitif potansiyeline sahip, ancak tüm vaat kütüphaneleri (yani çünkü bütün bunlar itimat olduğu varsayımıdır edebilirsiniz güvenmek). Görebildiğim kadarıyla tek alternatif, Benjamin Gruenbaum'un önerisini alıp söz test paketinden geçirmektir. Ancak bu gerçek üretim kodu için pratik değildir.
JLRishe

Yanıtlar:


342

Bir söz kütüphanesi nasıl karar verir

Bir .thenişlevi varsa - kütüphanelerin kullandığı tek standart vaat kütüphanesidir.

Promises / A + belirtimi then, temelde " thenyöntemli bir nesne" olan able adında bir nosyona sahiptir . Vaatler herhangi bir yöntemi bir yöntemle özümseyecek ve özümsemelidir . Bahsettiğiniz tüm söz uygulaması bunu yapıyor.

Şartnameye bakarsak :

2.3.3.3 thenbir işlevse, bunu x ile çağırın, ilk argüman resolPromise ve ikinci argüman rejectPromise

Ayrıca, bu tasarım kararının gerekçesini açıklar:

Bu thenables tedavisi, Promises / A + uyumlu bir thenyöntem ortaya koydukları sürece söz verilen uygulamaların birlikte çalışmasına izin verir . Ayrıca Promises / A + uygulamalarının uygun olmayan uygulamaları makul ve daha sonra yöntemlerle “özümsemesine” izin verir.

Nasıl karar vermelisin

Bunun yerine , herhangi bir değeri veya harici kabileyi her zaman güvenilir bir vaat haline dönüştürecek Promise.resolve(x)( Q(x)Q olarak) aramamalısınız. Bu kontrolleri kendiniz yapmaktan daha güvenli ve kolaydır.then

gerçekten emin olmalısın?

Her zaman test paketinden çalıştırabilirsiniz : D


168

Bir şeyin vaat edilip edilmediğini kontrol etmek kodu zorlaştırır, sadece kullanın Promise.resolve

Promise.resolve(valueOrPromiseItDoesntMatter).then(function(value) {

})

1
Promise.resolve yoluna çıkan her şeyi halledebilir mi? Kesinlikle bir şey değil, ama makul bir şey sanırım?
Alexander Mills

3
@AlexMills evet, jQuery vaadi gibi standart olmayan vaatlerde bile çalışır. Nesnenin vaatten tamamen farklı bir arayüze sahip bir then yöntemi varsa başarısız olabilir.
Esailija

19
Bu cevap, belki de iyi bir tavsiye olsa da, aslında soruya cevap vermez.
Stijn de Witt

4
Soru gerçekten bir söz kütüphanesi uygulayan biriyle ilgili değilse, soru geçersizdir. Sadece bir söz kütüphanesinin kontrolü yapması gerekir, bundan sonra her zaman gösterdiğim gibi .resolve yöntemini kullanabilirsiniz.
Esailija

4
@Esalija Soru, sadece bir söz kütüphanesinin uygulayıcısı için değil, alakalı ve önemli gibi görünüyor. Uygulamaların nasıl davranacağını / nasıl davranacağını / davranacağını ve farklı vaat kütüphanelerinin birbirleriyle nasıl etkileşime gireceğini bilmek isteyen bir söz kütüphanesi kullanıcısı için de geçerlidir . Özellikle, bu kullanıcı X'in "vaat" (burada "söz" ne olursa olsun - bu soru) dışında herhangi bir X için bir X vaat edebileceğim gerçeğiyle büyük ölçüde dehşete düşmüş durumda ve kesinlikle ilgileniyorum bu istisnanın sınırlarının tam olarak nerede olduğunu bilmek.
Don Hatch

104

İşte o zamandan beri bir vaat için test etme yolu olarak spesifikasyonda onaylanan orijinal cevabım :

Promise.resolve(obj) == obj

Bu çalışır çünkü algoritmaPromise.resolve , yalnızca spesifikasyonun tanımına göre bir söz ise, geçirilen tam nesneyi döndürmesi gerektiğini açıkça talep eder .

Burada başka bir cevabım var, bunu söylerdim, ama o sırada Safari ile çalışmadığında başka bir şeye değiştirdim. Bu bir yıl önceydi ve şimdi Safari'de bile güvenilir bir şekilde çalışıyor.

Şimdiye kadar bu cevapta değiştirilen çözüme orijinalinden daha fazla insanın oy vermiş olması göz önüne alındığında, orijinal cevabımı düzenledim. Bunun daha iyi bir cevap olduğuna inanıyorum ve umarım katılıyorsunuz.


10
Eğer kullanmalıyım ===yerine ==?
Neil S

12
Bu aynı alanda olmayan vaatler için de başarısız olacaktır.
Benjamin Gruenbaum

4
"Spesifikasyonun tanımına göre bir söz", "Promise.resolve () aracılığıyla oluşturulan bir söz ile aynı kurucu tarafından oluşturulan bir söz" anlamına gelecektir "- yani bu, örneğin; bir polyfilled Promise aslında bir Promise
VoxPelli

3
Bu cevap, hemen bir cevapla başlamaktan ziyade soruyu nasıl yorumladığınızı belirterek başlayabilirse, OP maalesef bunu net bir şekilde ortaya koymadı ve siz de yapmadıysanız, bu noktada OP, yazar ve okuyucu muhtemelen 3 farklı sayfadadır. Bahsettiğiniz dokümanda, "argüman bu kurucu tarafından üretilen bir söz ise " italik kısım çok önemlidir. Yanıtladığınız sorunun bu olduğunu belirtmek iyi olur. Ayrıca cevabınız bu kütüphanenin bir kullanıcısı için yararlıdır ancak uygulayıcı için yararlı değildir.
Don Hatch

1
Bu yöntemi kullanmayın, işte neden, daha fazla BenjaminGruenbaum'un amacı. gist.github.com/reggi/a1da4d0ea4f1320fa15405fb86358cff
ThomasReggi

61

Güncelleme: Bu artık en iyi cevap değil. Lütfen bunun yerine diğer cevabımı oyla .

obj instanceof Promise

yapmalı. Bunun yalnızca yerel es6 vaatleriyle güvenilir bir şekilde çalışabileceğini unutmayın.

Bir şim, bir vaat kütüphanesi veya vaat benzeri gibi davranan başka bir şey kullanıyorsanız .then, buradaki diğer cevaplarda gösterildiği gibi bir "değiştirilebilir" ( yöntemle herhangi bir şey ) test etmek daha uygun olabilir .


O zamandan bu yana olan bana işaret edilmiş olduğu Promise.resolve(obj) == objSafari'de alışkanlık çalışmalarını. instanceof PromiseBunun yerine kullanın .
pergel

2
Bu güvenilir bir şekilde çalışmaz ve problemi izlemem için çılgınca zorlanmama neden oldu. Diyelim ki es6.promise şimini kullanan bir kütüphaneniz var ve Bluebird'i bir yerde kullandığınızda problemleriniz olacak. Bu sorun benim için Chrome Canary'de geldi.
vaughan

1
Evet, bu cevap aslında yanlış. Burada tam olarak böyle zor bir sorun için sona erdi. Bunun obj && typeof obj.then == 'function'yerine gerçekten kontrol etmelisiniz , çünkü her türlü vaatle çalışacaktır ve aslında spesifikasyon tarafından önerilen ve uygulamalar / çoklu dolgular tarafından kullanılan yoldur. Yerli Promise.allörneğin tüm üzerinde çalışacak thenAbles, sadece diğer yerel vaat. Kodunuz da öyle. Bu instanceof Promiseiyi bir çözüm değil.
Stijn de Witt

2
İzleyen - Daha da kötü: ben şu anda bir sorun hata ayıklamak çalışıyorum sadece yerli sözler kullanarak 6.2.2 node.js console.log(typeof p, p, p instanceof Promise);bu çıktıyı üretir: object Promise { <pending> } false. Gördüğünüz gibi bu bir vaat yolunda - ama instanceof Promisetest geri dönüyor falsemu?
Mörre

2
Bu, aynı alanda olmayan vaatler için başarısız olacaktır.
Benjamin Gruenbaum

46
if (typeof thing.then === 'function') {
    // probably a promise
} else {
    // definitely not a promise
}

6
bir şey tanımlanmamışsa ne olur? şey ile buna karşı korunmanız gerekir && ...
mrBorna

iyi değil ama kesinlikle çok muhtemel; sorunun kapsamına da bağlıdır. % 100 defansif olarak yazmak genellikle açık uçlu API'lerde veya verilerin şeklinin / imzasının tamamen açık uçlu olduğunu bildiğiniz durumlarda geçerlidir.
rob2d

17

Verilen nesnenin bir ES6 Sözü olup olmadığını görmek için bu yüklemden yararlanabiliriz:

function isPromise(p) {
  return p && Object.prototype.toString.call(p) === "[object Promise]";
}

Calling toStringdoğrudan Object.prototypegetiri bir yerli dize temsilini ise verilen nesne türünün "[object Promise]"bizim durumumuzda. Bu, verilen nesnenin

  • Gibi yanlış pozitifleri atlar ..:
    • Aynı yapıcı adına sahip kendinden tanımlı nesne türü ("Promise").
    • toStringVerilen nesnenin kendi yazdığı yöntem.
  • Veya öğesinin aksineinstanceof birden çok ortam bağlamında (örneğin iframe'ler) çalışır isPrototypeOf.

Ancak, etiketi değiştirilmiş olan herhangi bir ana bilgisayar nesnesi geri dönebilir . Bu, projeye bağlı olarak amaçlanan sonuç olabilir veya olmayabilir (örneğin özel bir Promise uygulaması varsa).Symbol.toStringTag"[object Promise]"


Nesnenin yerel bir ES6 Promise'dan olup olmadığını görmek için şunları kullanabiliriz:

function isNativePromise(p) {
  return p && typeof p.constructor === "function"
    && Function.prototype.toString.call(p.constructor).replace(/\(.*\)/, "()")
    === Function.prototype.toString.call(/*native object*/Function)
      .replace("Function", "Promise") // replacing Identifier
      .replace(/\(.*\)/, "()"); // removing possible FormalParameterList 
}

Göre bu ve bu bölümde spec, fonksiyonun dize temsilini olmalıdır:

"işlev Tanımlayıcısı ( FormalParameterList opt ) { FunctionBody }"

yukarıda ele alınmaktadır. FunctionBody olan [native code]tüm büyük tarayıcılarda.

MDN: Function.prototype.toString

Bu, çoklu ortam bağlamlarında da çalışır.


12

Tam soruya bir cevap değil ama Node.js 10'da isPromisebir nesnenin yerel bir Promise olup olmadığını kontrol eden yeni bir util fonksiyonunun eklendiğini belirtmeye değer olduğunu düşünüyorum :

const utilTypes = require('util').types
const b_Promise = require('bluebird')

utilTypes.isPromise(Promise.resolve(5)) // true
utilTypes.isPromise(b_Promise.resolve(5)) // false

11

Bu nasıl graphql-js paket vaat algılar:

function isPromise(value) {
  return Boolean(value && typeof value.then === 'function');
}

valueişlevinizin döndürülen değeridir. Projemde bu kodu kullanıyorum ve şimdiye kadar hiçbir sorunum yok.



6

Kullandığınız durumda typescript , sana "tipi yüklem" özelliğini kullanabilirsiniz olduğunu eklemek istiyorum. Mantıksal doğrulamayı geri dönen bir fonksiyona sarmanız x is Promise<any>gerekir ve daktilo yapmak zorunda kalmazsınız. Aşağıda örneğimde, cbir söz veya c.fetch()yöntemi çağırarak bir söze dönüştürmek istediğim türlerimden biri .

export function toPromise(c: Container<any> | Promise<any>): Promise<any> {
    if (c == null) return Promise.resolve();
    return isContainer(c) ? c.fetch() : c;
}

export function isContainer(val: Container<any> | Promise<any>): val is Container<any> {
    return val && (<Container<any>>val).fetch !== undefined;
}

export function isPromise(val: Container<any> | Promise<any>): val is Promise<any> {
    return val && (<Promise<any>>val).then !== undefined;
}

Daha fazla bilgi: https://www.typescriptlang.org/docs/handbook/advanced-types.html


6

Zaman uyumsuz bir yöntemdeyseniz bunu yapabilir ve belirsizliklerden kaçınabilirsiniz.

async myMethod(promiseOrNot){
  const theValue = await promiseOrNot()
}

İşlev vaat döndürürse, bekleyecek ve çözümlenen değerle geri dönecektir. İşlev bir değer döndürürse, çözümlenmiş olarak kabul edilir.

İşlev bugün bir söz vermezse, yarın birini döndürürse veya zaman uyumsuz olarak bildirilirse, geleceğe dönük olacaksınız.


Bu eserler, uygun burada : "[beklenen] değer bir söz değilse, [bekliyoruz ifade] bir çözüme Promise değer dönüştürür ve bunun için bekler"
pqnet

Temel olarak kabul edilen cevapta önerilen şeydir, bunun dışında burada async-await sözdizimi kullanılırPromise.resolve()
B12Toaster

3
it('should return a promise', function() {
    var result = testedFunctionThatReturnsPromise();
    expect(result).toBeDefined();
    // 3 slightly different ways of verifying a promise
    expect(typeof result.then).toBe('function');
    expect(result instanceof Promise).toBe(true);
    expect(result).toBe(Promise.resolve(result));
});

2

Bu işlevi evrensel bir çözüm olarak kullanıyorum:

function isPromise(value) {
  return value && value.then && typeof value.then === 'function';
}

-1

Async işlevlerini ve hatta Promises'ı tespit etmek için güvenilir bir yol aradıktan sonra , aşağıdaki testi kullanarak sonuçlandırdım:

() => fn.constructor.name === 'Promise' || fn.constructor.name === 'AsyncFunction'

bunun alt sınıflarını Promiseoluşturup bunlara örnek oluşturursanız, bu test başarısız olabilir. bu, test etmeye çalıştığınız şeylerin çoğu için işe yarayacaktır.
theram

Kabul ettim, ama neden kimsenin vaatlerin alt sınıflarını yaratacağını anlamıyorum
Sebastien H.

fn.constructor.name === 'AsyncFunction'yanlış - bir şey bir zaman uyumsuz fonksiyon ve bir sözdür demektir - insanlar sözlerini alt sınıf, çünkü aynı zamanda işin garanti edilmez
Benjamin Gruenbaum

@BenjaminGruenbaum Yukarıdaki örnek çoğu durumda işe yarar, kendi alt sınıfınızı oluşturursanız, testleri adına eklemelisiniz
Sebastien H.

Yapabilirsiniz, ancak hangi nesneleri zaten biliyorsanız, vaatlerin olup olmadığını zaten biliyorsunuzdur.
Benjamin Gruenbaum

-3

ES6:

const promise = new Promise(resolve => resolve('olá'));

console.log(promise.toString().includes('Promise')); //true

2
toStringYöntemi olan (veya üzerine yazılan) herhangi bir nesne içeren bir dize döndürebilir "Promise".
Boghyon Hoffmann

4
Bu cevap birçok nedenden ötürü kötü, en belirgin varlık'NotAPromise'.toString().includes('Promise') === true
18'de
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.