JavaScript Promise'in durumunu eşzamanlı olarak nasıl belirleyebilirim?


151

Tamamen bir JavaScript Promise'im var (yerleşik uygulama veya çoklu doldurma):

var promise = new Promise(function (resolve, reject) { /* ... */ });

Gönderen şartname , bir Promise biri olabilir:

  • "yerleşik" ve "çözüldü"
  • "yerleşik" ve "reddedildi"
  • 'Bekliyor'

Sözü eşzamanlı olarak sorgulamak ve şunları belirlemek istediğim bir kullanım durumum var:

  • Söz yerine getirildi mi?

  • eğer öyleyse, Söz çözüldü mü?

#then()Promise değişiklikleri durumundan sonra eşzamansız olarak gerçekleştirilecek işi planlamak için kullanabileceğimi biliyorum . Bunu nasıl yapacağımı sormuyorum.

Bu soru özellikle bir Promise durumunun eşzamanlı olarak sorgulanmasıyla ilgilidir . Bunu nasıl başarabilirim?


6
vaatte dışarıdan görülebilecek bir özellik belirleyin ve ardından özelliği değiştirmek için () öğesini kullanın.
dandavis

FWIW @jokeyrhyme, V8 kaynak code.google.com/p/v8/source/browse/branches/bleeding_edge/src/... bakınız var promiseStatus = NEW_PRIVATE("Promise#status");, PromiseSetfonksiyonSET_PRIVATE(promise, promiseStatus, status);
guest271314

İşte başlıyoruz: esdiscuss.org/topic/…
jokeyrhyme

Eğer const a = Promise.resolve ('baz'); console.log (a); ve Chrome konsoluna bakın, Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "baz"} proto: Promise [[PromiseStatus]]: "resolved" [[PromiseValue]]: "baz "ve insanlar bunun yapılamayacağını iddia ediyor. Chrome bunu nasıl yapıyor? (bunu Angular plnkr.co/edit/IPIWgLJKQStI5ubXmcsF
JGFMK

V11.12.0 console.log düğümünün kullanılması söz verme durumunu gösterecektir. EG console.log(Promise.new((resolve, reject) => {})=>Promise { <pending> }
Puhlze

Yanıtlar:


80

Yerel JavaScript vaatleri için böyle bir eşzamanlı inceleme API'si yoktur. Bunu yerli vaatlerle yapmak imkansız. Spesifikasyon böyle bir yöntemi belirtmez.

Userland kitaplıkları bunu yapabilir ve belirli bir motoru (v8 gibi) hedefliyorsanız ve platform koduna erişiminiz varsa (yani, kodun çekirdeğini yazabilirsiniz ), bunu başarmak için belirli araçları (özel semboller gibi) kullanabilirsiniz. . Bu, kullanıcı dünyasında değil, çok spesifik olsa da.


4
Not: Senkronize inceleme için kullanım durumlarının çok az ve çok nadir olduğuna inanıyorum, eğer somut kullanım durumunuzu yeni bir soruyla eşzamanlı inceleme olmadan nasıl elde edeceğinizi soran paylaşırsanız - birisi yapmazsa bir şans vereceğim beat me to it :)
Benjamin Gruenbaum

4
Kullanım durumları nadir olsa bile, buna benzer bir şey dahil olmak üzere ne zarar verir? Önceki işin bitip bitmediğini ve başka bir iş isteyip isteyemeyeceğimi görmek için bunun gibi bir durum kontrolüne ihtiyacım olacak. Ve harici bir değişken ayarlayamam çünkü nesnenin önceden haber vermeden sahiplerini değiştirme potansiyeli var. Daha rahatsız edici olan şey ise, Node.js'nin bu bilgilere erişebildiğini, çünkü onu incelediğimde bana gösteriyor, ancak dizeleri ayrıştırmaktan başka bir yolu yok mu?
Tustin2121

9
Bu yüzden pratik olmadıkları ve her zaman mavi kuş kullandıkları için yerel vaatleri bir kenara atmalıyız. Harika haber! Yerel vaatlerin kullanımdan kaldırılması ve düğüm motorunun dışına atılmasını nasıl önerebilirim?
user619271

1
Pek çok şey, .anybunun yerine açıklayıp hata yapmalıydık çünkü Mark ısrar etti. Birincisi, Promise.race([])sonsuza dek bekleyen bir sözdür (ve bir hata değildir), genellikle sadece ilk sözü değil , ilk başarılı sözü istersiniz . Her neyse, bu sorulan soruyla pek alakalı değil - OP eşzamanlı denetim hakkında soru sordu .raceve bunun birçok eksikliğini değil.
Benjamin Gruenbaum

5
@Akrikos bu cevabı, bir sözün durumunu eşzamanlı olarak incelemenize izin vermiyor - Örneğin MakeQueryablePromise(Promise.resolve(3)).isResolvedyanlış ama söz oldukça açık bir şekilde çözüldü. Cevabın "çözüldü" ve "yerine getirildi" terimlerini de yanlış kullandığından bahsetmiyorum bile. Bu cevabı yapmak için, .thenkendiniz bir işleyici eklemeniz yeterli olur - bu, eşzamanlı inceleme noktasını tamamen kaçırır.
Benjamin Gruenbaum

32

Hayır, senkronizasyon API'si yok, ancak işte benim eşzamansız promiseStatesürümüm (@Matthijs'in yardımıyla):

function promiseState(p) {
  const t = {};
  return Promise.race([p, t])
    .then(v => (v === t)? "pending" : "fulfilled", () => "rejected");
}

var a = Promise.resolve();
var b = Promise.reject();
var c = new Promise(() => {});

promiseState(a).then(state => console.log(state)); // fulfilled
promiseState(b).then(state => console.log(state)); // rejected
promiseState(c).then(state => console.log(state)); // pending


Bu yapının arkasında belirli bir gerekçe var mı? Bana gereksiz yere karmaşık görünüyor. Anlayabildiğim kadarıyla, bu aynı şekilde çalışıyor: Promise.race([ Promise.resolve(p).then(() => "fulfilled", () => "rejected"), Promise.resolve().then(() => "pending") ]); Bu bana daha güvenli görünse de: const t = {}; return Promise.race([p,t]).then(v => v === t ? "pending" : "fulfilled", () => "rejected") ve orijinal p beklemede olduğu sürece devam eden ek vaatler oluşturmaktan kaçınıyor.
Matthijs

Teşekkürler @Matthijs! Cevabımı basitleştirdim.
jib

31

görüntü açıklamasını buraya girin

promise-status-async hile yapar. Zaman uyumsuzdur, ancak thensözün çözülmesini beklemek için kullanılmaz.

const {promiseStatus} = require('promise-status-async');
// ...
if (await promiseStatus(promise) === 'pending') {
    const idle = new Promise(function(resolve) {
        // can do some IDLE job meanwhile
    });
    return idle;
}

6
OP eşzamanlı olarak nasıl yapılacağını sordu
Klesun

16

Promise.resolve ile yarış yapabilirsiniz.
Eşzamanlı değil ama şimdi oluyor

function promiseState(p, isPending, isResolved, isRejected) {
  Promise.race([p, Promise.resolve('a value that p should not return')]).then(function(value) {
    if (value == 'a value that p should not return') {
      (typeof(isPending) === 'function') && isPending();
    }else {
      (typeof(isResolved) === 'function') && isResolved(value);
    }
  }, function(reason) {
    (typeof(isRejected) === 'function') && isRejected(reason);
  });
}

Eşzamansız anlamlarını test etmek ve anlamak için küçük bir betik

var startTime = Date.now() - 100000;//padding trick "100001".slice(1) => 00001
function log(msg) {
  console.log((""+(Date.now() - startTime)).slice(1) + ' ' + msg);
  return msg;//for chaining promises
};

function prefix(pref) { return function (value) { log(pref + value); return value; };}

function delay(ms) {
  return function (value) {
    var startTime = Date.now();
    while(Date.now() - startTime < ms) {}
    return value;//for chaining promises
  };
}
setTimeout(log, 0,'timeOut 0 ms');
setTimeout(log, 100,'timeOut 100 ms');
setTimeout(log, 200,'timeOut 200 ms');

var p1 = Promise.resolve('One');
var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "Two"); });
var p3 = Promise.reject("Three");

p3.catch(delay(200)).then(delay(100)).then(prefix('delayed L3 : '));

promiseState(p1, prefix('p1 Is Pending '), prefix('p1 Is Resolved '), prefix('p1 Is Rejected '));
promiseState(p2, prefix('p2 Is Pending '), prefix('p2 Is Resolved '), prefix('p2 Is Rejected '));
promiseState(p3, prefix('p3 Is Pending '), prefix('p3 Is Resolved '), prefix('p3 Is Rejected '));

p1.then(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : '));
p2.then(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : '));
p3.catch(prefix('Level 1 : ')).then(prefix('Level 2 : ')).then(prefix('Level 3 : '));
log('end of promises');
delay(100)();
log('end of script');

gecikmeli sonuçlar (0) (while in delay)

00001 end of promises
00001 end of script
00001 Level 1 : One
00001 Level 1 : Three
00001 p1 Is Resolved One
00001 p2 Is Pending undefined
00001 p3 Is Rejected Three
00001 Level 2 : One
00001 Level 2 : Three
00001 delayed L3 : Three
00002 Level 3 : One
00002 Level 3 : Three
00006 timeOut 0 ms
00100 timeOut 100 ms
00100 Level 1 : Two
00100 Level 2 : Two
00101 Level 3 : Two
00189 timeOut 200 ms

ve firefox ile yapılan bu testin sonuçları (Chrome siparişi koru)

00000 end of promises
00100 end of script
00300 Level 1 : One
00300 Level 1 : Three
00400 p1 Is Resolved One
00400 p2 Is Pending undefined
00400 p3 Is Rejected Three
00400 Level 2 : One
00400 Level 2 : Three
00400 delayed L3 : Three
00400 Level 3 : One
00400 Level 3 : Three
00406 timeOut 0 ms
00406 timeOut 100 ms
00406 timeOut 200 ms
00406 Level 1 : Two
00407 Level 2 : Two
00407 Level 3 : Two

promiseState make .race ve .then: Seviye 2


3
Bunun yerine, 'a value that p should not return'bir Symbol
programmer5000

1
@ programmer5000 Faydası nedir?
Moritz Schmitz / Hülst

2
@ MoritzSchmitzv.Hülst a Symbolbenzersiz bir değer olacaktır, bu nedenle "değer [...] p'nin dönmemesi gerektiğini" asla tahmin etmek zorunda kalmazsınız. Bununla birlikte, belirli bir nesneye yapılan referans da aynı şekilde işe yarar.
Scott Rudiger

8

Yerel bir yöntem sunulana kadar Node.js'de (çirkin) bir hack kullanabilirsiniz:

util = require('util');

var promise1 = new Promise (function (resolve) {
}

var promise2 = new Promise (function (resolve) {

    resolve ('foo');
}

state1 = util.inspect (promise1);
state2 = util.inspect (promise2);

if (state1 === 'Promise { <pending> }') {

    console.log('pending'); // pending
}

if (state2 === "Promise { 'foo' }") {

    console.log ('foo') // foo
}

3
Bunu bir Promise.prototype.isPending = function(){ return util.inspect(this).indexOf("<pending>")>-1; }
çoklu dolgu haline getirdim

5
Bu korkunç .
John Weisz

@JohnWeisz Korkunç olan, geriye dönük uyumluluğun olmaması. Her şeyin senkronize olduğunu varsayan bir kod tabanına umut veren bir API'yi entegre etmeye çalışıyorum. Ya korkunç bir şey yapıyor ya da büyük kod parçalarını yeniden yazıyor. Her iki durumda da bir gaddarlık yapıyorum.
rath

4
sadece kullanprocess.binding('util').getPromiseDetails
amara

@ Tustin2121 Bazı sürümler için gibi bir şeyle başarısız olacaktır Promise.resolve('<pending>').
kullanıcı202729

7

düğümde, belgelenmemiş dahili diyelim process.binding('util').getPromiseDetails(promise)

> process.binding('util').getPromiseDetails(Promise.resolve({data: [1,2,3]}));
[ 1, { data: [ 1, 2, 3 ] } ]

> process.binding('util').getPromiseDetails(Promise.reject(new Error('no')));
[ 2, Error: no ]

> process.binding('util').getPromiseDetails(new Promise((resolve) => {}));
[ 0, <1 empty item> ]

bunu ekledim çünkü mevcut cevapların hiçbirinde yoktu ve düğüm için en iyi cevap bu. bunun için dokümanlara github.com/nodejs/node adresinden
amara

6

Güncelleme: 2019

Bluebird.js şunu sunar: http://bluebirdjs.com/docs/api/isfulfilled.html

var Promise = require("bluebird");
let p = Promise.resolve();
console.log(p.isFulfilled());

Kendi sarmalayıcınızı oluşturmayı tercih ederseniz, işte bununla ilgili güzel bir blog .

JavaScript tek iş parçacıklı olduğundan, bunu spesifikasyona koymayı haklı çıkarmak için yeterince yaygın bir kullanım durumu bulmak zordur. Bir sözün çözülüp çözülmediğini bilmek için en iyi yer .then () içindedir. Bir Sözün yerine getirilip getirilmediğini test etmek, büyük olasılıkla yanlış yön olan bir yoklama döngüsü oluşturur.

async / await , eşzamansız kodu eşzamanlı olarak kullanmak istiyorsanız güzel bir yapıdır.

await this();
await that();
return 'success!';

Başka bir kullanışlı çağrı Promise.all ()

var promise1 = Promise.resolve(3);
var promise2 = 42;
var promise3 = new Promise(function(resolve, reject) {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3]).then(function(values) {
  console.log(values);
});
// expected output: Array [3, 42, "foo"]

Bu cevaba ilk ulaştığımda, aradığım kullanım durumu buydu.


5

Sözlerini bu şekilde tamamlayabilirsin

function wrapPromise(promise) {
  var value, error,
      settled = false,
      resolved = false,
      rejected = false,
      p = promise.then(function(v) {
        value = v;
        settled = true;
        resolved = true;
        return v;
      }, function(err) {
        error = err;
        settled = true;
        rejected = true;
        throw err;
      });
      p.isSettled = function() {
        return settled;
      };
      p.isResolved = function() {
        return resolved;
      };
      p.isRejected = function() {
        return rejected;
      };
      p.value = function() {
        return value;
      };
      p.error = function() {
        return error;
      };
      var pThen = p.then, pCatch = p.catch;
      p.then = function(res, rej) {
        return wrapPromise(pThen(res, rej));
      };
      p.catch = function(rej) {
        return wrapPromise(pCatch(rej));
      };
      return p;
}

5
Bu, OP'nin olay döngüsünün önceki bir dönüşündeki söze erişmesini gerektirir . Yana .thenhep bir söz incelemek istiyor uyumsuz OP yürütür aynı sırayla buraya doğru sonucu almazsınız. Not OP, özellikle eşzamanlı denetim hakkında sorular sordu ve eşzamansız denetimi zaten bildiklerinden bahsetti.
Benjamin Gruenbaum

@BenjaminGruenbaum: Aynı "dönüş" üzerindeki kod onu çağırırsa varsayılan değerler gelmez mi?
dandavis

Elbette tüm sözlerinizi oluşturma sırasında tamamlamanız gerekir. örneğin onları oluşturan ve döndüren işlevlerin içinde.
SpiderPig 01

3
Doğru, bu noktada artık gerçek vaatler değiller, onları bir nesneye maymun yamalama özellikleri yerine bunu zarif bir şekilde yapmanıza izin verecek alt sınıflandırma ile genişletilmeleri gerektiği şekilde genişletebilirsiniz.
Benjamin Gruenbaum

Bir sözü gösterdiğim şekilde veya alt sınıflara ayırarak uzatsanız da, her durumda yine de kendi o zamanın versiyonunu eklemeniz ve yakalamanız gerekir.
SpiderPig 01

5

Bu temel işlevin eksik olması gerçekten can sıkıcı. Eğer node.js kullanıyorsanız, iki geçici çözüm biliyorum, ikisi de çok hoş değil. Aşağıdaki her iki snippet de aynı API'yi uygular:

> Promise.getInfo( 42 )                         // not a promise
{ status: 'fulfilled', value: 42 }
> Promise.getInfo( Promise.resolve(42) )        // fulfilled
{ status: 'fulfilled', value: 42 }
> Promise.getInfo( Promise.reject(42) )         // rejected
{ status: 'rejected', value: 42 }
> Promise.getInfo( p = new Promise(() => {}) )  // unresolved
{ status: 'pending' }
> Promise.getInfo( Promise.resolve(p) )         // resolved but pending
{ status: 'pending' }

Her iki numarayı da kullanarak son iki vaat durumunu ayırt etmenin bir yolu yok gibi görünüyor.

1. V8 hata ayıklama API'sini kullanın

Bu, kullanılan numara ile aynıdır util.inspect.

const Debug = require('vm').runInDebugContext('Debug');

Promise.getInfo = function( arg ) {
    let mirror = Debug.MakeMirror( arg, true );
    if( ! mirror.isPromise() )
        return { status: 'fulfilled', value: arg };
    let status = mirror.status();
    if( status === 'pending' )
        return { status };
    if( status === 'resolved' )  // fix terminology fuck-up
        status = 'fulfilled';
    let value = mirror.promiseValue().value();
    return { status, value };
};

2. Mikro görevleri eşzamanlı olarak çalıştırın

Bu, hata ayıklama API'sini önler, ancak bekleyen tüm mikro görevlerin ve process.nextTickgeri aramaların eşzamanlı olarak çalıştırılmasına neden olarak bazı korkutucu anlamlara sahiptir . Ayrıca, denetlenen söz için "işlenmemiş söz reddi" hatasının tetiklenmesini engelleyen bir yan etkiye de sahiptir.

Promise.getInfo = function( arg ) {
    const pending = {};
    let status, value;
    Promise.race([ arg, pending ]).then(
        x => { status = 'fulfilled'; value = x; },
        x => { status = 'rejected'; value = x; }
    );
    process._tickCallback();  // run microtasks right now
    if( value === pending )
        return { status: 'pending' };
    return { status, value };
};

Bunu yapmak çok güvenli değildir process._tickCallback(hatta% RunMicrotick bile) - kodunuzdaki şeyleri rastgele kıracaktır. Çaresizce onu çalıştırmaya çalıştım (çoğunlukla zaman uyumsuz işlevlerdeki sahte zamanlayıcılar için) ve Düğüm tarafından asla yeterince kararlı olmadı. Bunun üzerinde çalışmaktan bir şekilde vazgeçtim. V8 hata ayıklama aynası API'si burada tamamen uygundur.
Benjamin Gruenbaum

Ve .. DeprecationWarning: DebugContext has been deprecated and will be removed in a future version.:( Görünüşe göre V8 onu kaldırmış
Benjamin Gruenbaum

Biz (Node), V8'den tamamen bir API isteyebilir veya bir vaadin durumuna doğrudan bakmak için bir API'yi açığa çıkarabiliriz - github.com/nodejs/promise-use-cases adresinde bir sorunu açarsanız , bunu mutlu bir şekilde V8 ile gündeme getireceğim
Benjamin Gruenbaum

1
Bu konunun daha altındaki bir yorum, bir API'nin zaten var gibi göründüğünü ortaya çıkardı: beklemede, tamamlanmış ve reddedilmiş için process.binding('util').getPromiseDetails( promise )iadeler . [ 0, ][ 1, value ][ 2, value ]
Matthijs

3

Uyarı: Bu yöntem, belgelenmemiş Node.js iç bileşenlerini kullanır ve uyarı yapılmadan değiştirilebilir.

Düğümde kullanarak bir sözün durumunu eşzamanlı olarak belirleyebilirsiniz process.binding('util').getPromiseDetails(/* promise */);.

Bu geri dönecek:

[0, ] beklemede

[1, /* value */] yerine getirilmesi için veya

[2, /* value */] reddedildi.

const pending = new Promise(resolve => setTimeout(() => resolve('yakko')));;
const fulfilled = Promise.resolve('wakko');
const rejected = Promise.reject('dot');

[pending, fulfilled, rejected].forEach(promise => {
  console.log(process.binding('util').getPromiseDetails(promise));
});

// pending:   [0, ]
// fulfilled: [1, 'wakko']
// rejected:  [2, 'dot']

Bunu bir yardımcı işleve sarmak:

const getStatus = promise => ['pending', 'fulfilled', 'rejected'][
  process.binding('util').getPromiseDetails(promise)[0]
];

getStatus(pending); // pending
getStatus(fulfilled); // fulfilled
getStatus(rejected); // rejected

İçimden işe yaramıyor gibi görünüyor jest(gerçekten ilgilendiğim tek yer burası). İşlev var, ancak her zaman geri dönüyor gibi görünüyor undefined. Sorunun ne olduğunu nasıl anlarım?
Adam Barnes

Hmm, içinde çalıştığını hatırlıyorum mocha; asla denemedim jest. Belki buraya bağlanarak yeni bir soru başlatabilir ve Node.js sürümünüzün yanı sıra jestsürümünüzü de ekleyebilirsiniz ?
Scott Rudiger

Maalesef artık pek ilgilendiğim bir şey değil. Temelde Promise, yalnızca a Promisebeklemede iken devam etmesi gereken şeyleri test etmek için kullandığım manuel olarak çözülebilir / reddedilebilir cihazımı aklı başında test etmek istiyordum , ancak yazdıklarım, işe yaradığı sürece bunu test etmeye gerek olmadığını düşündüm. ona neyin dayandığına ek olarak.
Adam Barnes

2

Yapabileceğiniz şey, durumu depolamak için bir değişken kullanmak, durumu manuel olarak o değişkene ayarlamak ve o değişkeni kontrol etmektir.

var state = 'pending';

new Promise(function(ff, rjc) {
  //do something async

  if () {//if success
    state = 'resolved';

    ff();//
  } else {
    state = 'rejected';

    rjc();
  }
});

console.log(state);//check the state somewhere else in the code

Tabii ki bu, sözün orijinal koduna erişiminizin olması gerektiği anlamına gelir. Yapmazsanız, şunları yapabilirsiniz:

var state = 'pending';

//you can't access somePromise's code
somePromise.then(function(){
  state = 'resolved';
}, function() {
  state = 'rejected';
})

console.log(state);//check the promise's state somewhere else in the code

Benim çözümüm daha fazla kodlama, ancak muhtemelen kullandığınız her söz için bunu yapmanız gerekmeyeceğini düşünüyorum.


2

Node.js sürüm 8'den itibaren, yerel vaatleri eşzamanlı olarak incelemek için artık akıllı denetim paketini kullanabilirsiniz (herhangi bir tehlikeli hack olmadan).


2

Promise.prototype'a bir yöntem ekleyebilirsiniz. Şöyle görünüyor:

Düzenlendi: İlk çözüm, buradaki yanıtların çoğu gibi düzgün çalışmıyor. Eşzamansız işlev ". Sonra" çağrılana kadar "beklemede" döndürür, bu hemen gerçekleşmez. (Aynısı Promise.race kullanan çözümlerle ilgili). İkinci çözümüm bu sorunu çözüyor.

if (window.Promise) {
    Promise.prototype.getState = function () {
        if (!this.state) {
            this.state = "pending";
            var that = this;
            this.then(
                function (v) {
                    that.state = "resolved";
                    return v;
                },
                function (e) {
                    that.state = "rejected";
                    return e;
                });
        }
        return this.state;
    };
}

Herhangi bir Sözde kullanabilirsiniz. Örneğin:

myPromise = new Promise(myFunction);
console.log(myPromise.getState()); // pending|resolved|rejected

İkinci (ve doğru) çözüm:

if (window.Promise) {
    Promise.stateable = function (func) {
        var state = "pending";
        var pending = true;
        var newPromise = new Promise(wrapper);
        newPromise.state = state;
        return newPromise;
        function wrapper(resolve, reject) {
            func(res, rej);
            function res(e) {
                resolve(e);
                if (pending) {
                    if (newPromise)
                        newPromise.state = "resolved";
                    else
                        state = "resolved";
                    pending = false;
                }
            }
            function rej(e) {
                reject(e);
                if (pending) {
                    if (newPromise)
                        newPromise.state = "rejected";
                    else
                        state = "rejected";
                    pending = false;
                }
            }
        }
    };
}

Ve onu kullanın:

Uyarı : Bu çözümde "yeni" operatörünü kullanmanız gerekmez.

myPromise = Promise.stateable(myFunction);
console.log(myPromise.state); // pending|resolved|rejected

2

awaitdeyimsel prototipleme ile @ jib cevabının kullanımı .

Object.defineProperty(Promise.prototype, "state", {
    get: function(){
        const o = {};
        return Promise.race([this, o]).then(
            v => v === o ? "pending" : "resolved",
            () => "rejected");
    }
});

// usage: console.log(await <Your Promise>.state);
(async () => {
    console.log(await Promise.resolve(2).state);  // "resolved"
    console.log(await Promise.reject(0).state);   // "rejected"
    console.log(await new Promise(()=>{}).state); // "pending"
})();

bu eşzamansız işlevin "hemen hemen" eşitlenmiş işlev gibi (veya muhtemelen anında) çalıştırdığını unutmayın.


1

İşte QueryablePromise'ın daha ayrıntılı bir es6 sürümü, ilk çözümlemeden sonra zincirleme ve yakalama ve api'yi yerel Promise ile tutarlı tutmak için hemen çözme veya reddetme yeteneğine izin verir.

const PROMISE = Symbol('PROMISE')
const tap = fn => x => (fn(x), x)
const trace = label => tap(x => console.log(label, x))

class QueryablePromise {
  resolved = false
  rejected = false
  fulfilled = false
  catchFns = []
  constructor(fn) {
    this[PROMISE] = new Promise(fn)
      .then(tap(() => {
        this.fulfilled = true
        this.resolved = true
      }))
      .catch(x => {
        this.fulfilled = true
        this.rejected = true
        return Promise.reject(x)
      })
  }
  then(fn) {
    this[PROMISE].then(fn)
    return this
  }
  catch(fn) {
    this[PROMISE].catch(fn)
    return this
  }
  static resolve(x) {
    return new QueryablePromise((res) => res(x))
  }
  static reject(x) {
    return new QueryablePromise((_, rej) => rej(x))
  }
}

const resolvedPromise = new QueryablePromise((res) => {
  setTimeout(res, 200, 'resolvedPromise')
})

const rejectedPromise = new QueryablePromise((_, rej) => {
  setTimeout(rej, 200, 'rejectedPromise')
})

// ensure our promises have not been fulfilled
console.log('test 1 before: is resolved', resolvedPromise.resolved)
console.log('test 2 before: is rejected', rejectedPromise.rejected)


setTimeout(() => {
  // check to see the resolved status of our promise
  console.log('test 1 after: is resolved', resolvedPromise.resolved)
  console.log('test 2 after: is rejected', rejectedPromise.rejected)
}, 300)

// make sure we can immediately resolve a QueryablePromise
const immediatelyResolvedPromise = QueryablePromise.resolve('immediatelyResolvedPromise')
  // ensure we can chain then
  .then(trace('test 3 resolved'))
  .then(trace('test 3 resolved 2'))
  .catch(trace('test 3 rejected'))

// make sure we can immediately reject a QueryablePromise
const immediatelyRejectedPromise = QueryablePromise.reject('immediatelyRejectedPromise')
  .then(trace('test 4 resolved'))
  .catch(trace('test 4 rejected'))
<script src="https://codepen.io/synthet1c/pen/KyQQmL.js"></script>


1

2019:

Bildiğim gibi bunu yapmanın basit yolu thenable, söz veya herhangi bir zaman uyumsuz iş etrafında süper ince bir ambalaj.

const sleep = (t) => new Promise(res => setTimeout(res,t));
const sleeping = sleep(30);

function track(promise){
    let state = 'pending';
    promise = promise.finally( _=> state ='fulfilled');
    return {
        get state(){return state},
        then: promise.then.bind(promise), /*thentable*/
        finally:promise.finally.bind(promise),
        catch:promise.catch.bind(promise),
    }
}


promise = track(sleeping);
console.log(promise.state) // pending

promise.then(function(){
    console.log(promise.state); // fulfilled
})

1

Şunları yapabilirsiniz extendPromise sınıf yeni oluşturmak için queryable Promise sınıfını.

Kendi alt sınıfınızı, örneğin , vaat nesnelerinin durumunu eşzamanlı olarak sorgulamak için kullanabileceğiniz örnekleri üzerinde mevcut bir özelliğe sahip olan QueryablePromiseyerel olarak mevcut Promisesınıftan devralarak oluşturabilirsiniz . Bunun bir uygulaması aşağıda görülen veya atıfta edilebilir bu daha iyi bir açıklama için.status

class QueryablePromise extends Promise {
  constructor (executor) {
    super((resolve, reject) => executor(
      data => {
        resolve(data)
        this._status = 'Resolved'
      },
      err => {
        reject(err)
        this._status = 'Rejected'
      },
    ))
    this._status = 'Pending'
  }

  get status () {
    return this._status
  }
}
 
// Create a promise that resolves after 5 sec 
var myQueryablePromise = new QueryablePromise((resolve, reject) => {
  setTimeout(() => resolve(), 5000)
})

// Log the status of the above promise every 500ms
setInterval(() => {
  console.log(myQueryablePromise.status)
}, 500)


Maalesef mevcut API bu yeni sınıfı döndürmeyecek. İnsanların bunu nasıl kullandığını hayal ediyorsunuz?
jib

@jib Cevabınız için teşekkürler. Hiçbir API'nin bu sınıfı geri vermeyeceği ne demek? :(
UtkarshPramodGupta

Mevcut API'lerin hiçbiri onu geri vermeyecek, çünkü iade etmek için yazılmaları gerekecek, değil mi? Örneğin, ararsam fetchyerel bir söz vereceğim. Sınıfınız buna nasıl yardımcı olur?
jib

Peki, bu getirme çağrısını yeni QuerablePromise'mizde şu şekilde kapatamaz mıyız const queryableFetch = new QueryablePromise((resolve, reject) => {fetch(/.../).then((data) => resolve(data)) })? Yoksa bununla ilgili bir sorun mu var? : /
UtkarshPramodGupta

Bu işe , err => reject(err)yaramalı , sadece ikinci bir argüman olarak unutmayın then, yoksa hataları doğru bir şekilde yaymayacaktır (bunun nedenleri arasında vaat yapıcı anti-model olarak kabul edilir ). Yine de tam olarak eşzamanlı değildir (örneğin, önceden çözülmüş bir sözü algılamaz), ancak arayan kişiyi kontrol etmediğiniz ve yanıta hemen ihtiyaç duyulduğu durumlarda yararlı olabilir.
jib

1

Başka var zarif bir söz hala dizeye bütün nesneyi dönüştürerek sadece bekleyen ve bunun gibi incelemek yardımı ile kontrol edilir, denetimi ve hacky yolu: util.inspect(myPromise).includes("pending").

Node.js 8,9,10,11,12,13 üzerinde test edildi

İşte tam bir örnek

const util = require("util")

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

(async ()=>{
  let letmesleep = sleep(3000)
  setInterval(()=>{
    console.log(util.inspect(letmesleep).includes("pending"))
  },1000)
})()

Sonuç:

true
true
false
false
false


0

ES7 deneysel kullanıyorsanız, dinlemek istediğiniz sözü kolayca tamamlamak için eşzamansız kullanabilirsiniz.

async function getClient() {
  let client, resolved = false;
  try {
    client = await new Promise((resolve, reject) => {
      let client = new Client();

      let timer = setTimeout(() => {
         reject(new Error(`timeout`, 1000));
         client.close();
      });

      client.on('ready', () => {
        if(!resolved) {
          clearTimeout(timer);
          resolve(client);
        }
      });

      client.on('error', (error) => {
        if(!resolved) {
          clearTimeout(timer);
          reject(error);
        }
      });

      client.on('close', (hadError) => {
        if(!resolved && !hadError) {
          clearTimeout(timer);
          reject(new Error("close"));
        }
      });
    });

    resolved = true;
  } catch(error) {
    resolved = true;
    throw error;
  }
  return client;
}

0

Küçük bir npm paketi yazdım, promise-value, resolvedbayrak içeren bir söz sarmalayıcısı sağlar :

https://www.npmjs.com/package/promise-value

Ayrıca taahhüt değerine (veya hataya) eşzamanlı erişim sağlar. Bu, genişletme deseninden ziyade sarmayı izleyerek Promise nesnesinin kendisini değiştirmez.


0

Bu daha eski bir soru ama ben de benzer bir şey yapmaya çalışıyordum. N sayıda çalışanı devam ettirmem gerekiyor. Bir sözle yapılandırılırlar. Taramam ve çözülüp çözülmediğini, reddedilip reddedilmediğini veya hala beklemede olup olmadığını görmem gerekiyor. Çözülürse, değere ihtiyacım var, reddedilirse sorunu düzeltmek için bir şeyler yapın veya beklemede. Çözülürse veya reddedilirse, devam etmek için başka bir göreve başlamam gerekir. Promise.all veya Promise.race ile bunu yapmanın bir yolunu bulamıyorum çünkü bir dizide vaatleri çalıştırmaya devam ediyorum ve onları silmenin bir yolunu bulamıyorum. Bu yüzden hile yapan bir işçi yaratıyorum

Gerektiği gibi çözen veya reddeden bir söz veren bir söz oluşturma işlevine ihtiyacım var. Sözün ne yaptığını bilmek için çerçeveyi kuran bir işlev tarafından çağrılır.

Aşağıdaki kodda, jeneratör basitçe setTimeout'a dayalı bir söz verir.

İşte burada

//argObj should be of form
// {succeed: <true or false, nTimer: <desired time out>}
function promiseGenerator(argsObj) {
  let succeed = argsObj.succeed;          
  let nTimer = argsObj.nTimer;
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (succeed) {
        resolve('ok');
      }
      else {
        reject(`fail`);
      }
    }, nTimer);
  })

}

function doWork(generatorargs) {
  let sp = { state: `pending`, value: ``, promise: "" };
  let p1 = promiseGenerator(generatorargs)
    .then((value) => {
      sp.state = "resolved";
      sp.value = value;
    })
    .catch((err) => {
      sp.state = "rejected";
      sp.value = err;
    })
  sp.promise = p1;
  return sp;
}

doWork, sözü ve durumunu ve döndürülen değeri içeren bir nesne döndürür.

Aşağıdaki kod, durumu test eden ve onu 3 çalışan işçi olarak tutmak için yeni çalışanlar oluşturan bir döngü çalıştırır.

let promiseArray = [];

promiseArray.push(doWork({ succeed: true, nTimer: 1000 }));
promiseArray.push(doWork({ succeed: true, nTimer: 500 }));
promiseArray.push(doWork({ succeed: false, nTimer: 3000 }));

function loopTimerPromise(delay) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('ok');
    }, delay)
  })
}

async function looper() {
  let nPromises = 3;      //just for breaking loop
  let nloop = 0;          //just for breaking loop
  let i;
  //let continueLoop = true;
  while (true) {
    await loopTimerPromise(900);  //execute loop every 900ms
    nloop++;
    //console.log(`promiseArray.length = ${promiseArray.length}`);
    for (i = promiseArray.length; i--; i > -1) {
      console.log(`index ${i} state: ${promiseArray[i].state}`);
      switch (promiseArray[i].state) {
        case "pending":
          break;
        case "resolved":
          nPromises++;
          promiseArray.splice(i, 1);
          promiseArray.push(doWork({ succeed: true, nTimer: 1000 }));
          break;
        case "rejected":
          //take recovery action
          nPromises++;
          promiseArray.splice(i, 1);
          promiseArray.push(doWork({ succeed: false, nTimer: 500 }));
          break;
        default:
          console.log(`error bad state in i=${i} state:${promiseArray[i].state} `)
          break;
      }
    }
    console.log(``);
    if (nloop > 10 || nPromises > 10) {
      //should do a Promise.all on remaining promises to clean them up but not for test
      break;
    }
  }
}

looper();

Node.js'de test edildi

BTW Bu cevapta çok değil ama benzer konulardaki diğerlerinde, birisinin "anlamıyorsun" veya "böyle çalışmıyor" demesinden nefret ediyorum Genelde soruyu soranın ne istediğini bildiğini varsayıyorum. Daha iyi bir yol önermek harika. Vaatlerin nasıl çalıştığına dair sabırlı bir açıklama da iyi olurdu.


0

Pek çok yanıtı olan eski soru, ancak hiçbiri en basit çözüm olduğunu düşündüğüm şeyi önermiyor gibi görünüyor: vaatlerin çözülmesi / reddedilmesi konusunda bir bool göstergesi belirleyin.

class Promise2 {
  constructor(...args) {
    let promise = new Promise(...args);
    promise.then(() => promise._resolved_ = true);
    promise.catch(() => promise._rejected_ = true);
    return promise;
  }
}

let p = new Promise2(r => setTimeout(r, 3000));

setInterval(() => {
  console.log('checking synchronously if p is resolved yet?', p._resolved_);
}, 1000);


-1

Bu çözümü basit buldum ve yerel vaatleri kullanmaya devam etmeme izin verirken yararlı eşzamanlı kontroller ekledim. Ayrıca söz veren kütüphanenin tamamını toplamak zorunda değildim.

CAVEAT: Bu, yalnızca mevcut yürütme iş parçacığında sözlerin senkronize yapıları kontrol etmeden ÖNCE yürütülmesine izin vermek için bir tür kesinti varsa çalışır. Bu, bunu başlangıçta düşündüğümden daha sınırlı kullanışlı hale getiriyor - yine de kullanım durumum için yararlı ( Bunu işaret ettiği için teşekkürler Benjamin Gruenbaum )

/**
 * This function allow you to modify a JS Promise by adding some status properties.
 * Based on: http://stackoverflow.com/questions/21485545/is-there-a-way-to-tell-if-an-es6-promise-is-fulfilled-rejected-resolved
 * But modified according to the specs of promises : https://promisesaplus.com/
 */
function MakeQuerablePromise(promise) {
    // Don't modify any promise that has been already modified.
    if (promise.isFulfilled) return promise;

    // Set initial state
    var isPending = true;
    var isRejected = false;
    var isFulfilled = false;

    // Observe the promise, saving the fulfillment in a closure scope.
    var result = promise.then(
        function(v) {
            isFulfilled = true;
            isPending = false;
            return v; 
        }, 
        function(e) {
            isRejected = true;
            isPending = false;
            throw e; 
        }
    );

    result.isFulfilled = function() { return isFulfilled; };
    result.isPending = function() { return isPending; };
    result.isRejected = function() { return isRejected; };
    return result;
}

wrappedPromise = MakeQueryablePromise(Promise.resolve(3)); 
setTimeout(function() {console.log(wrappedPromise.isFulfilled())}, 1);

Gönderen https://ourcodeworld.com/articles/read/317/how-to-check-if-a-javascript-promise-has-been-fulfilled-rejected-or-resolved hangi yanıtlarını dayalı İş'in orada bir yol için ES6 sözünün yerine getirilip getirilmediğini / reddedildiğini / çözülüp çözülmediğini söyle?


Cevabım hakkındaki yorumunuzda da eklendiği gibi - bu tamamen yanlıştır: bu, bir sözün durumunu eşzamanlı olarak incelemenize izin vermez - Örneğin MakeQueryablePromise(Promise.resolve(3)).isResolvedyanlıştır ancak söz oldukça açık bir şekilde çözülmüştür. Cevabın "çözüldü" ve "yerine getirildi" terimlerini de yanlış kullandığından bahsetmiyorum bile. Bu cevabı yapmak için, .thenkendiniz bir işleyici eklemeniz yeterli olur - bu, eşzamanlı inceleme noktasını tamamen kaçırır.
Benjamin Gruenbaum

Ne dediğini anlıyorum ve iyi bir noktaya değindin. JS'nin tek iş parçacıklı doğası engel oluyor, değil mi? Sözün çözüldü olarak işaretlenmesi için mevcut uygulamaya bir ara vermelisiniz. let wrappedPromise = MakeQueryablePromise(Promise.resolve(3)); setTimeout(function() {console.log(wrappedPromise.isFulfilled())}, 1);Bunu yaptığın sürece, bu iyi çalışıyor. Ancak bunun yararlı olması için bu gerçeği anlamalısınız. Açıklamayı bu uyarı ile güncelleyeceğim. Ayrıca, işlev isimlendirmesinin daha iyi / daha deyimsel olabileceğine de katılıyorum.
Akrikos

Ama bu noktada, sadece thenorijinal sözü verebilir ve aynı şeyi başarabilirsiniz çünkü bu, eşzamansızdır. process.binding('util').getPromiseDetailsİşe yarayan bir yol var ama özel bir API kullanıyor
Benjamin Gruenbaum

Her zaman buna sahip olmak iğrençtir ve kodun anlaşılmasını çok daha zor hale getirir. Özellikle tek umursadığım sözün reddedilip reddedilmediği olduğunda - bu yüzden seçeneklerim ya bu durumu başka bir yerde saklamak ya da böyle bir şey yapmak. Kendi çözümümü göndermeden önce buradaki diğer çözümleri tam olarak okumadığımı kabul ediyorum - bunun için özür dilerim. Bu sorun ilk başta düşündüğümden daha yapışkan.
Akrikos
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.