Jak mogę synchronicznie określić stan obietnicy JavaScript?

Mam czystą obietnicę JavaScript (wbudowana implementacja lub poly-fill):

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

Ze specyfikacji obietnica może być jedną z:

  • 'rozstrzygnięty' i 'rozstrzygnięty'
  • "rozstrzygnięty" i "odrzucony"
  • "oczekujące"

Mam przypadek użycia, w którym chcę przesłuchać obietnicę synchronicznie i ustalić:

  • Czy obietnica została dotrzymana?

  • Jeśli tak, to czy obietnica rozwiązany?

I know that I can use #then() aby zaplanować pracę do wykonania asynchronicznie po zmianie stanu obietnicy. Nie pytam, jak to zrobić.

To pytanie dotyczy w szczególności synchronicznego przesłuchania stanu obietnicy . Jak mogę to osiągnąć?

Author: Kaivosukeltaja, 2015-06-01

25 answers

Nie istnieje takie synchroniczne API inspekcji dla natywnych obietnic JavaScript.Nie da się tego zrobić z rodzimymi obietnicami. Specyfikacja nie określa takiej metody.

Biblioteki Userland mogą to zrobić, a jeśli kierujesz na konkretny silnik (jak v8) i masz dostęp do kodu platformy (to znaczy, możesz napisać kod w core), możesz użyć określonych narzędzi (takich jak prywatne symbole), aby to osiągnąć. To jest bardzo konkretne chociaż i nie w userland.

 84
Author: Benjamin Gruenbaum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-01 00:26:48

Nie, Nie ma sync API, ale oto moja wersja async promiseState (z pomocą @Matthijs):

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
 38
Author: jib,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-25 23:04:38

Tutaj wpisz opis obrazka

Promise-status-async załatwia sprawę. Jest asynchroniczny, ale nie używa then, aby czekać na rozwiązanie obietnicy.

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;
}
 32
Author: 0xaB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-25 01:55:29

Możesz się ścigać z obietnicą.resolve
To nie jest synchroniczne, ale dzieje się teraz

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

Mały skrypt do testowania i zrozumienia ich znaczenia asynchronicznie

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

Wyniki z opóźnieniem (0) (skomentuj chwilę z opóźnieniem)

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

I wyniki tego testu z Firefoksem (chrome zachować kolejność)

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 .wyścig i .następnie: Poziom 2

 17
Author: Steween,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-01 05:50:10

Możesz użyć (brzydkiego) hack w Node.js do momentu zaoferowania natywnej metody:

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
}
 9
Author: rabbitco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-10 16:20:10

Aktualizacja: 2019

Bluebird.js oferuje to: http://bluebirdjs.com/docs/api/isfulfilled.html
var Promise = require("bluebird");
let p = Promise.resolve();
console.log(p.isFulfilled());

Jeśli wolisz stworzyć własny wrapper, oto fajny blog o tym.

Ponieważ JavaScript jest jednowątkowy, trudno znaleźć wystarczająco powszechny przypadek użycia, aby uzasadnić umieszczenie tego w specyfikacji. Najlepsze miejsce, aby wiedzieć, czy obietnica jest rozwiązana jest w .następnie(). Testowanie, czy obietnica jest wypełniona, stworzy pętlę wyborczą, która najprawdopodobniej zły kierunek.

Async / wait jest ładną konstrukcją, jeśli chcesz synchronicznie rozumować kod asynchroniczny.

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

Innym użytecznym wywołaniem jest Promise.wszystkie()

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"]

Kiedy po raz pierwszy sięgnąłem po tę odpowiedź, to jest przypadek użycia, którego szukałem.

 7
Author: Michael Cole,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

In node, say undocumented internal 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> ]
 7
Author: amara,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-05-31 13:20:46

Możesz zapakować swoje obietnice w ten sposób

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
Author: SpiderPig,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-01 00:41:15

To rzeczywiście dość irytujące, że brakuje tej podstawowej funkcjonalności. Jeśli używasz node.js wtedy znam dwa obejścia, żaden z nich nie jest bardzo ładny. Oba poniższe fragmenty implementują to samo API:

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

Wydaje się, że nie ma sposobu na rozróżnienie dwóch ostatnich Stanów obietnicy za pomocą obu sztuczek.

1. Korzystanie z interfejsu API debugowania V8

Jest to ta sama sztuczka, której używa 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. Synchronously run microtasks

Pozwala to uniknąć debug API, ale ma przerażającą semantykę, powodując synchroniczne uruchamianie wszystkich oczekujących mikrotasek i wywołań zwrotnych process.nextTick. Ma również efekt uboczny, zapobiegając kiedykolwiek wywołaniu błędu" odrzucenia nieobsługiwanej obietnicy " dla sprawdzonej obietnicy.

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 };
};
 5
Author: Matthijs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-12 05:36:20

Możesz dodać metodę do obietnicy.prototyp. Wygląda to tak:

Edytowane: pierwsze rozwiązanie nie działa poprawnie, jak większość odpowiedzi tutaj. Zwraca "Oczekujące" dopóki funkcja asynchroniczna".wtedy " jest wywoływany, co nie dzieje się natychmiast. (To samo dotyczy rozwiązań wykorzystujących obietnicę.wyścig). Moje drugie rozwiązanie rozwiązuje ten problem.

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;
    };
}
Możesz go użyć przy każdej obietnicy. Dla exemple:
myPromise = new Promise(myFunction);
console.log(myPromise.getState()); // pending|resolved|rejected

Drugi (i poprawny) rozwiązanie:

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

I użyj go:

Uwaga: w tym rozwiązaniu nie musisz używać operatora "nowy".

myPromise = Promise.stateable(myFunction);
console.log(myPromise.state); // pending|resolved|rejected
 3
Author: Moshe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-10 13:58:25

Zastrzeżenie: Ta metoda wykorzystuje nieudokumentowany węzeł.js wewnętrzne i może być zmieniony bez ostrzeżenia.

W Node można synchronicznie określić stan obietnicy za pomocą process.binding('util').getPromiseDetails(/* promise */);.

To zwróci:

[0, ] for pending,

[1, /* value */] dla spełnionych, lub

[2, /* value */] dla odrzuconych.

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']

Zawijanie tego do funkcji pomocniczej:

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

getStatus(pending); // pending
getStatus(fulfilled); // fulfilled
getStatus(rejected); // rejected
 3
Author: Scott Rudiger,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-03-08 06:34:23

await Użycie do @ jib ' s answer , z idiomatycznym prototypowaniem.

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

Zauważ, że ta funkcja asynchroniczna wykonuje" prawie " natychmiast, jak funkcja synchronizowana (lub faktycznie może być natychmiast).

 3
Author: Valen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-10-25 15:10:28

Możesz użyć zmiennej do przechowywania stanu, ręcznie ustawić stan na tę zmienną i sprawdzić tę zmienną.

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

Oczywiście, oznacza to, że musisz mieć dostęp do oryginalnego kodu obietnicy. Jeśli nie, to możesz zrobić:

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

Moim rozwiązaniem jest więcej kodowania, ale myślę, że prawdopodobnie nie musiałbyś tego robić za każdą obietnicę, której używasz.

 2
Author: BigName,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-08 22:00:57

Od węzła.js w wersji 8, możesz teraz użyć pakietu wise-inspection do synchronicznego sprawdzania natywnych obietnic (bez żadnych niebezpiecznych hacków).

 2
Author: Joshua Wise,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-21 05:30:35

Istnieje innyelegancki & hacky sposób sprawdzania, czy obietnica jest nadal oczekująca, wystarczy przekonwertować cały obiekt na string i sprawdzić go za pomocą inspect w następujący sposób: util.inspect(myPromise).includes("pending").

Testowane na Node.js 8,9,10,11,12,13

Oto pełny przykład

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

Wynik:

true
true
false
false
false

 2
Author: devio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-05 21:59:44

Oto bardziej rozbudowana wersja ES6 QueryablePromise, pozwalająca na łączenie się następnie i łapanie po pierwszym rozwiązaniu i natychmiastowe rozwiązywanie lub odrzucanie, aby api było zgodne z natywną obietnicą.

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
Author: synthet1c,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-02-01 06:44:11

2019:

Prosty sposób na to, jak Wiem, to thenable , super cienka owijka wokół obietnicy lub dowolna praca asynchroniczna.

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
Author: pery mimon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-02 15:22:23

Możesz extend klasy Promise stworzyć nowąqueryable Promise klasy.

Możesz utworzyć własną podklasę, powiedzmy QueryablePromise, dziedzicząc z natywnie dostępnej klasy Promise, której instancje mają dostępną właściwość status, której możesz użyć do zapytania statusu obiektów promise synchronicznie. Implementację można zobaczyć poniżej lub zapoznać się z to dla lepszego wyjaśnienia.

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)
 1
Author: UtkarshPramodGupta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-25 11:53:37

Jeśli używasz ES7 experimental, możesz użyć async, aby łatwo zawinąć obietnicę, którą chcesz słuchać.

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
Author: Ezequiel S. Pereira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-11 15:05:46

Napisałem mały pakiet npm, promise-value, który zapewnia wrapper promise z flagą resolved:

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

Daje również synchroniczny dostęp do wartości obietnicy (lub błędu). Nie zmienia to samego obiektu obietnicy, postępując zgodnie ze schematem zawijania, a nie rozszerzania.

 0
Author: Daniel Winterstein,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-29 10:16:20

To jest starsze pytanie, ale próbowałem zrobić coś podobnego. Muszę utrzymać pracowników N. Są one skonstruowane w obietnicy. Muszę zeskanować i sprawdzić, czy są rozwiązane, odrzucone lub nadal oczekujące. Jeśli rozwiązany, potrzebuję wartość, jeśli odrzucone zrobić coś, aby naprawić problem lub oczekujące. Jeśli zostanie rozwiązany lub odrzucony, muszę rozpocząć inne zadanie, aby utrzymać N. Nie wiem, jak to zrobić z obietnicą.wszystko albo obietnica.wyścig jak dotrzymuję obietnic pracy w tablicy i nie mogę znaleźć sposobu aby je usunąć. Więc tworzę pracownika, który robi sztuczkę

Potrzebuję funkcji generatora obietnic, która zwraca obietnicę, która rozwiązuje lub odrzuca w razie potrzeby. Jest wywoływana przez funkcję, która tworzy ramy, aby wiedzieć, co robi obietnica.

W kodzie poniżej generator po prostu zwraca obietnicę opartą na setTimeout.

Tutaj jest

//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 zwraca obiekt zawierający obietnicę oraz jej stan i zwracaną wartość.

The następujący kod uruchamia pętlę, która testuje stan i tworzy nowych pracowników, aby utrzymać go na 3 uruchomionych pracowników.

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

Testowane w node.js

BTW Nie w tej odpowiedzi tyle, ale w innych na podobne tematy, nienawidzę, gdy ktoś mówi "nie rozumiesz" lub "tak to nie działa" generalnie zakładam, że pytający wie czego chce. Sugerowanie lepszego sposobu jest świetne. Cierpliwe wyjaśnienie, jak obietnice działają, byłoby również dobre.

 0
Author: Charles Bisbee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-11 03:19:59

Stare pytanie z wieloma odpowiedziami, ale żadne nie wydaje się sugerować, co myślę, że jest najprostszym rozwiązaniem: ustawić wskaźnik bool na obiecanej rozdzielczości / odrzucenia.

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);
 0
Author: junvar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-08-06 15:42:14

Przejrzałem rozwiązania zaproponowane na to pytanie i nie widziałem takiego, który odpowiada prostemu podejściu, które zastosowałem w Node.js.

Zdefiniowałem prostą klasę PromiseMonitor, która pobiera obietnicę jako pojedynczy parametr do swojego konstruktora i ma właściwość string .status, która zwraca standardowe wartości łańcuchowe odpowiadające statusowi promise, "oczekujący", "rozwiązany" lub "odrzucony" oraz cztery właściwości logiczne .pending, .resolved, .rejected i .error. Nieruchomość {[7] } jest ustawione jako true tylko wtedy, gdy .rejected jest true i odrzucone wywołanie zwrotne zostało przekazane jako obiekt błędu.

Klasa po prostu używa .then() na obietnicy, aby zmienić status PromiseMonitor, gdy obietnica zostanie rozwiązana lub odrzucona. Nie koliduje to z żadnym innym wykorzystaniem pierwotnej obietnicy. Oto kod:

class PromiseMonitor {
    constructor(prm){
        this._status = "pending";
        this._pending = true;
        this._resolved = false;
        this._rejected = false;
        this._error = false;
        prm
            .then( ()=>{  
                        this._status = "resolved"; 
                        this._resolved = true; 
                        this._pending = false; 
                    } 
                , (err)=>{ 
                        this._status = "rejected";
                        this._pending = false;
                        this._rejected = true;
                        this._error = err instanceof Error ? true: false ; 
                    } 
                );
    }

    get status(){ return this._status; };
    get pending(){ return this._pending; };
    get resolved(){ return this._resolved; };
    get rejected(){ return this._rejected; };
    get error(){ return this._error };
};

Aby monitorować status obietnicy, wystarczy utworzyć instancję PromiseMonitor, przekazując obietnicę jako parametr, dla przykład:

let promiseObject = functionThatReturnsAPromise();
let promiseMonitor = new PromiseMonitor( promiseObject );

Teraz możesz zsynchronizować wszystkie właściwości promiseMonitor, które będą śledzić status oryginalnej obietnicy. Oto skrypt testowy, który demonstruje trzy możliwe rozdzielczości monitorowanej obietnicy.

let ticks = 0;
let tickerID = setInterval( ()=>{++ticks; console.log(`..tick ${ticks}`)}, 1000);

async function run(){
    console.log("Start");

    let delay = prmDelay(2000);
    let delayMonitor = new PromiseMonitor(delay);

    // normal handling of delay promise
    delay.then((result)=>( console.log("Normal resolution of delay using .then()") ) );

    console.log("delay at start:\n", delay);
    console.log("delayMonitor at start:\n", delayMonitor);
    await delay;
    console.log("delay finished:\n", delay);
    console.log("delayMonitor finished:\n", delayMonitor);


    console.log("\n\n TEST2: Rejection without an Error test ================================")
    let rejDelay = prmDelay(3000, "reject");
    let rejMonitor = new PromiseMonitor(rejDelay);

    // normal handling of reject result on promise
    rejDelay.then((result)=>( console.log("Normal resolution of rejDelay using .then will not happen") ) 
                    , (err)=>( console.log("Rejection of rejDelay handled using .then")));

    console.log("rejDelay at start:\n", rejDelay);
    console.log("rejMonitor at start:\n", rejMonitor);

    await rejDelay.catch( (err)=>{ console.log( "Caught error using .catch on rejDelay" ); });

    console.log("rejDelay finished:\n", rejDelay);
    console.log("rejMonitor finished:\n", rejMonitor);


    console.log("\n\n TEST3: Rejection with an Error test ================================")
    let errMonitor ;
    let errDelay;
    try{

        errDelay = prmDelay(1000, "error");
        errMonitor = new PromiseMonitor(errDelay);
        
        // normal handling of results of the original promise
        errDelay.then(
            (result)=>{ 
                console.log("Normal expiry of errDelay");
                console.log("Monitor Status is " + errMonitor.status )
            } 
            , (err)=>{
                console.log("** Rejection of errDelay handled using .then()");
                console.log("   Monitor Status is " + errMonitor.status )
            }
        );

        console.log("errDelay at start:\n", errDelay);
        console.log("errMonitor at start:\n", errMonitor);

        await errDelay;

        console.log("**** This should never be run");

    } catch(err) { 

        console.log( "** Caught error on errDelay using try{}catch{}:" ); 
        console.log( "   Monitor Status is " + errMonitor.status )

    };

    console.log("errDelay finished:\n", errDelay);
    console.log("errMonitor finished:\n", errMonitor);
    

    clearInterval(tickerID);


}

/**
 * Creates a new promise with a specific result
 * @param {*} tt 
 * @param {*} exitType ("resolve", "reject" or "error")
 */
function prmDelay (tt, exitType) {
    
    return new Promise(function(resolve, reject) {
        if( exitType == 'reject' ){
            setTimeout(()=>{ reject("REJECTED")}, tt);
        } else if( exitType== 'error'){
            setTimeout(()=>{ reject(new Error( "ERROR Rejection") ); }, tt);
        } else {
            setTimeout(()=>{ resolve("RESOLVED") }, tt);
        } ;
    });
};


run();
 0
Author: JohnRC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-12-20 16:07:16

To jest Future wzór, którego używam: ( https://github.com/Smallscript-Corp )

  • umożliwia sync i async użycie fn
  • umożliwia zunifikowanie wzorców zdarzeńz zachowaniem
class XPromise extends Promise {
  state = 'pending'
  get settled() {return(this.state !== 'pending')}
  resolve(v,...a) {
    this.state = 'resolved'
    return(this.resolve_(this.value = v,...a))
  }
  reject(e,...a) {
    this.state = 'rejected'
    return(this.reject_(this.value = (e instanceof Error) ? e : XPromise.Error(e),...a))
  }
  static Error(e) {const v = Error('value-rejected'); v.value = e; return(v)}
  static Future(fn,...args) { // FactoryFn
    let r,t,fv = new XPromise((r_,t_) => {r=r_;t=t_})
    fv.resolve_ = r; fv.reject_  = t;
    switch(typeof fn) {
      case 'undefined': break; case 'function': fn(fv,...args); break;
      default: fv.resolve(fn)
    }
    return(fv)
  }
}
global.Future = XPromise.Future

Następnie można utworzyć instancje future-value , które mogą być rozwiązywane za pomocą funkcji synchronizacji i asynchronizacji; umożliwia obsługę zdarzeń równomiernie.

Możesz go użyć do napisania wzoru:

async doSomething() {
  // Start both - logically async-parallel
  const fvIsNetworkOnLine = this.fvIsNetworkOnline
  const fvAuthToken = this.fvAuthToken
  // await both (order not critical since both started/queued above)
  await fvAuthToken
  await fvIsNetworkOnLine
  // ... we can check the future values here if needed `fv.resolved`, `fv.state` etc
  // ... do dependent workflow here ...
}
onNetworkOnLine(fIsOnline) {
  // We utilize the `fv.settled` below, and use the event to `settle` it etc
  if(fIsOnline) {
    if(this.fvNetworkAvailable_)
      this.fvNetworkAvailable_.resolve(true)
    this.fvNetworkAvailable_ = undefined
  }
  else if(this.fvNetworkAvailable_.settled) {
    this.fvNetworkAvailable_ = undefined
  }
}
get fvNetworkAvailable() {
  if(navigator.onLine)
    return true
  else if(this.fvNetworkAvailable_)
    return this.fvNetworkAvailable_
  return (this.fvNetworkAvailable_ = Future())
}
get fvAuthToken() {
  if(this.fvAuthToken_)
    return this.fvAuthToken_
  const authTokenFv = async fv => {
    // ... handle retry logic etc here ...
  }
  return(this.fvAuthToken_ = Future(authTokenFv))
}
 0
Author: smallscript,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2021-01-13 00:01:18

Okazało się, że to rozwiązanie jest proste i pozwala mi nadal używać natywnych obietnic, ale dodać przydatne kontrole synchroniczne. Nie musiałem też wyciągać całej biblioteki obietnic.

Zastrzeżenie: to działa tylko wtedy, gdy w bieżącym wątku wykonania jest jakaś przerwa, która pozwala na wykonanie obietnic przed sprawdzeniem konstrukcji synchronicznych. To sprawia, że jest to bardziej ograniczona przydatność, niż początkowo myślałem - nadal przydatne dla mojego przypadku użycia chociaż (dzięki {4]}Benjamin Gruenbaum za wskazanie tego)

/**
 * 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);

Od https://ourcodeworld.com/articles/read/317/how-to-check-if-a-javascript-promise-has-been-fulfilled-rejected-or-resolved na której podstawie oparli swoją odpowiedź czy istnieje sposób, aby stwierdzić, czy obietnica ES6 jest spełniona/odrzucona / rozwiązana?

 -1
Author: Akrikos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-21 22:30:12