JavaScript Promise의 상태를 동 기적으로 어떻게 확인할 수 있습니까?


149

순수한 JavaScript Promise (내장 구현 또는 폴리 필)가 있습니다.

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

로부터 사양 , 약속은 다음 중 하나 일 수 있습니다 :

  • '정착'및 '해결'
  • '정착'및 '거부'
  • '보류 중'

약속을 동 기적으로 조사하고 다음을 결정하려는 유스 케이스가 있습니다.

  • 약속이 정 해졌습니까?

  • 그렇다면 약속이 해결 되었습니까?

#then()약속 변경 상태 후에 작업이 비동기 적으로 수행되도록 예약 하는 데 사용할 수 있다는 것을 알고 있습니다 . 이 작업을 수행하는 방법을 묻지 않습니다.

이 질문은 특히 Promise 상태의 동기식 심문에 관한 것 입니다. 어떻게하면 되나요?


6
외부에서 볼 수있는 약속에 속성을 설정하고 then ()을 사용하여 속성을 변경하십시오.
dandavis

FWIW @jokeyrhyme, V8 소스 code.google.com/p/v8/source/browse/branches/bleeding_edge/src/...은 참조 var promiseStatus = NEW_PRIVATE("Promise#status");, PromiseSet의 함수SET_PRIVATE(promise, promiseStatus, status);
guest271314

여기에 우리가 간다 : esdiscuss.org/topic/...
jokeyrhyme

const a = Promise.resolve ( 'baz'); console.log (a); Chrome 콘솔에서 Promise {[[PromiseStatus]] : "resolved", [[PromiseValue]] : "baz"} proto : Promise [[PromiseStatus]] : "resolved"[[PromiseValue]] : "baz가 표시됩니다. 사람들은 그것을 할 수 없다고 주장합니다. Chrome은 어떻게하고 있습니까? (각도와 Plunker에서이 일을했다 plnkr.co/edit/IPIWgLJKQStI5ubXmcsF
JGFMK

노드 v11.12.0 console.log를 사용하면 약속 상태가 표시됩니다. EG console.log(Promise.new((resolve, reject) => {})=>Promise { <pending> }
Puhlze 2016 년

답변:


77

기본 JavaScript 약속에 대한 동기 검사 API는 없습니다. 기본 약속으로는 불가능합니다. 사양에서는 이러한 방법을 지정하지 않습니다.

Userland 라이브러리가이를 수행 할 수 있으며 v8과 같은 특정 엔진을 대상으로하고 플랫폼 코드에 액세스 할 수있는 경우 (즉, 핵심 코드를 작성할 수있는 경우) 개인 도구와 같은 특정 도구를 사용하여이를 달성 할 수 있습니다. . 그것은 userland가 아니라 매우 구체적입니다.


4
참고 : 동기 검사 없이 사용 방법을 묻는 새로운 질문에 구체적인 유스 케이스를 공유하면 동기 검사의 사용 사례는 거의 없으며 매우 드물다고 믿습니다. 누군가가 그렇지 않으면 기회에 대답하겠습니다. :) 그것에 나를 이길
벤자민 Gruenbaum

4
유스 케이스가 드물더라도 이와 같은 것을 포함하여 어떤 피해가 발생합니까? 이전 작업이 완료되었는지와 다른 작업을 요청할 수 있는지 확인하려면 이와 같은 상태 확인이 필요합니다. 그리고 객체가 예고없이 소유자를 변경할 가능성이 있기 때문에 외부 변수를 설정할 수 없습니다. 더 짜증나는 것은 SEE가 검사 할 때 나에게 보여주기 때문에 Node.js 가이 정보에 액세스 할 수 있다는 것입니다.하지만 문자열을 구문 분석하는 것 외에는 정보를 얻을 수있는 방법이 없습니다.
Tustin2121

9
따라서 비현실적이고 항상 블루 버드를 사용하므로 기본 약속을 버려야합니다. 좋은 소식! 더 이상 사용되지 않고 노드 엔진에서 폐기 될 기본 약속을 어떻게 제안합니까?
user619271

1
.any마크가 주장하기 때문에 많은 것들, 대신 우리는 specspeced 하고 실수를해야합니다. 하나 Promise.race([])는, 영원히 계류중인 약속이며 (오류는 아님), 일반적으로 첫 번째 약속뿐만 아니라 첫 번째 성공적인 약속을 원합니다 . 어쨌든, 그것은 실제로 묻는 질문과 관련이 없습니다. OP는 동기 검사에 대해 물었고 .race많은 단점이 아닙니다 .
Benjamin Gruenbaum

5
@Akrikos가 대답하면 약속 상태를 동 기적으로 검사 할 수 없습니다. 예를 들어 MakeQueryablePromise(Promise.resolve(3)).isResolved거짓이지만 약속은 분명히 해결되었습니다. 대답은 또한 "해결됨"및 "완료 됨"이라는 용어를 잘못 사용하고 있습니다. 그렇게하려면 .then처리기를 직접 추가하면 동기 검사의 요점을 완전히 놓칠 수 있습니다.
Benjamin Gruenbaum

31

여기에 이미지 설명을 입력하십시오

promise-status-async 가 트릭을 수행합니다. 비동기이지만 then약속이 해결되기를 기다리는 데 사용하지 않습니다 .

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

4
OP는 그러나 동기식으로 수행하는 방법에 대해 물었다
Klesun

28

아니요, 동기화 API는 없지만 promiseState@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


이 구성 뒤에 구체적인 이유가 있습니까? 불필요하게 나에게는 복잡해 보인다. 내가 똑같이 작동 Promise.race([ Promise.resolve(p).then(() => "fulfilled", () => "rejected"), Promise.resolve().then(() => "pending") ]); 한다고 말할 수있는 한 : 이것은 나에게 더 안전 해 보이지만 : const t = {}; return Promise.race([p,t]).then(v => v === t ? "pending" : "fulfilled", () => "rejected") 원래의 p가 보류중인 한 지속 가능한 추가 약속을 만드는 것을 피합니다.
Matthijs

@Matthijs 감사합니다! 나는 대답을 단순화했다.
jib

16

Promise.resolve로 경쟁 할 수 있습니다.
동기 적이지는 않지만 지금 발생합니다.

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

비동기 적으로 그들의 의미를 테스트하고 이해하기위한 작은 스크립트

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

delay (0)의 결과 (지연된 동안 주석 처리)

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

파이어 폭스 (크롬 순서를 유지) 와이 테스트의 결과

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 및 .then : 레벨 2


3
대신에 'a value that p should not return', 용도 기호
programmer5000

1
@ programmer5000 이점은 무엇입니까?
Moritz Schmitz v. Hülst

2
@ MoritzSchmitzv.Hülst a Symbol는 고유 한 값이므로 "value [...] p가 반환되지 않아야하는 항목"을 추측 할 필요가 없습니다. 그러나 특정 객체에 대한 참조도 잘 작동합니다.
Scott Rudiger

7

기본 메소드가 제공 될 때까지 Node.js에서 (못생긴) 핵을 사용할 수 있습니다.

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
나는 polyfill로 내려 삶은 것 :Promise.prototype.isPending = function(){ return util.inspect(this).indexOf("<pending>")>-1; }
Tustin2121

5
그건 끔찍한 .
John Weisz

@JohnWeisz 끔찍한 것은 이전 버전과의 호환성이 없다는 것입니다. 약속의 API를 모든 것이 동 기적이라고 가정하는 코드베이스에 통합하려고합니다. 끔찍한 일을하거나 엄청난 양의 코드를 다시 작성하고 있습니다. 어느 쪽이든 나는 난관을 저지르고있다.
rath

4
그냥 사용process.binding('util').getPromiseDetails
amara

@ Tustin2121 일부 버전에서는 다음과 같이 실패 Promise.resolve('<pending>')합니다.
user202729

7

노드에서 문서화되지 않은 내부 라고 말하십시오. 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> ]

기존 답변에 없기 때문에 이것을 추가했으며 노드의 경우 가장 좋습니다. github.com/nodejs/node
amara

6

업데이트 : 2019

Bluebird.js는 다음을 제공합니다. http://bluebirdjs.com/docs/api/isfulfilled.html

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

자신 만의 래퍼를 만들고 싶다면 여기에 좋은 블로그 가 있습니다.

JavaScript는 단일 스레드이기 때문에 사양에 넣는 것을 정당화하기에 충분한 일반적인 사용 사례를 찾기가 어렵습니다. 약속이 해결되었는지 알 수있는 가장 좋은 곳은 .then ()입니다. 약속이 꽉 찼는 지 테스트하면 폴링 루프가 만들어져 잘못된 방향 일 가능성이 큽니다.

async / await 는 비동기 코드를 동 기적으로 추론하려는 경우 훌륭한 구성입니다.

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

또 다른 유용한 호출은 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"]

이 답변에 처음 도달했을 때, 이것이 내가 찾던 사용 사례입니다.


5

이런 방식으로 약속을 감쌀 수 있습니다

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
이것은 이벤트 루프의 이전 차례에서 약속 액세스하기 위해 OP가 필요 합니다 . 같은 차례에.then 약속을 검사하려는 OP는 항상 비동기식으로 실행 하기 때문에 올바른 결과를 얻지 못합니다. 참고 OP는 동기 검사 에 대해 구체적으로 요청 했으며 비동기 검사에 대해 이미 알고 있다고 언급했습니다.
Benjamin Gruenbaum 2016 년

@ BenjaminGruenbaum : 동일한 "턴"에 코드를 호출하면 기본값이 나타나지 않습니까?
dandavis 2016 년

물론 모든 약속을 창조 할 때 포장해야합니다. 예를 들어 함수를 생성하고 반환하는 함수 내부.
SpiderPig

3
그렇습니다.이 시점에서 더 이상 네이티브 약속이 아닌 경우 서브 클래스로 확장하여 객체의 원숭이 패치 속성 대신 우아하게 수행 할 수있는 방식으로 확장 할 수 있습니다.
Benjamin Gruenbaum 2016 년

내가 보여준 방식으로 또는 서브 클래 싱으로 약속을 확장하든, 각각의 경우 여전히 자신 만의 버전을 추가하고 잡아야합니다.
SpiderPig

5

이 기본 기능이 없다는 것은 실제로 성가신 일입니다. node.js를 사용하는 경우 두 가지 해결 방법을 알고 있습니다. 아래 두 스 니펫은 동일한 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' }

두 가지 트릭을 사용하여 마지막 두 약속 상태를 구별하는 방법이없는 것 같습니다.

1. V8 디버그 API 사용

이것은 같은 트릭입니다 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. 마이크로 태스크를 동기식으로 실행

이렇게하면 디버그 API를 피할 수 있지만 보류중인 모든 마이크로 태스크와 process.nextTick콜백을 동 기적으로 실행 하여 약간의 의미가 있습니다. 또한 "처리되지 않은 약속 거부"오류가 검사 된 약속에 대해 트리거되지 않도록하는 부작용이 있습니다.

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

그렇게하는 것은 매우 안전하지 않으며 process._tickCallback(또는 평범한 % RunMicrotick), 코드에서 무작위로 문제를 일으킬 것입니다. 필사적으로 작동하도록 노력했지만 (주로 비동기 기능의 가짜 타이머의 경우) 노드 측에서 충분히 안정적이지 않았습니다. 나는 그 일을 포기했다. 여기서는 V8 디버그 미러 API가 전적으로 적합합니다.
Benjamin Gruenbaum

그리고 .. DeprecationWarning: DebugContext has been deprecated and will be removed in a future version.: (V8이 제거 된 것 같습니다
Benjamin Gruenbaum

우리는 (노드) 완전히하는 API에 대한 V8을 요청하거나 약속의 상태를 직접하지만보고에 대한 API를 노출 할 수 있습니다 - 당신이에서 문제를 열면 github.com/nodejs/promise-use-cases 나는 행복하게 V8 함께 나타납니다
벤자민 Gruenbaum

1
이 주제에 대한 추가 의견은 API가 이미 존재하는 것으로 나타났습니다. 보류, 이행 및 거부에 대한 process.binding('util').getPromiseDetails( promise )리턴 . [ 0, ][ 1, value ][ 2, value ]
Matthijs

3

주의 사항 :이 방법은 문서화되지 않은 Node.js 내부를 사용하며 경고없이 변경 될 수 있습니다.

노드에서을 사용하여 약속의 상태를 동 기적으로 결정할 수 있습니다 process.binding('util').getPromiseDetails(/* promise */);.

이것은 다음을 반환합니다 :

[0, ] 보류 중

[1, /* value */] 성취 또는

[2, /* value */] 거부되었습니다.

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

이것을 도우미 함수로 감싸기 :

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

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

내부에서 작동하지 않는 것 같습니다 jest(내가 관심이있는 유일한 곳입니다). 이 함수는 존재하지만 항상을 반환하는 것 같습니다 undefined. 무엇이 잘못되었는지 어떻게 알 수 있습니까?
Adam Barnes

흠, 나는 그것이 내면에서 일하는 것을 기억한다 mocha. jest그래도 시도하지 않았습니다 . 어쩌면 여기에 링크하는 새로운 질문을 시작하고 Node.js 버전과 jest버전을 포함 하시겠습니까?
Scott Rudiger 19 :

불행히도 더 이상 내가 관심이있는 것은 아닙니다. 나는 기본적으로 수동으로 해결할 수있는 / 거부 가능한 상태를 위생 테스트 Promise하려고하고 Promise있었지만 보류 중일 때 진행되고있는 것들을 테스트하기 위해 사용하고 있었지만, 내가 쓴 것이 작동하는 한, 테스트 할 필요가 없었습니다. 그것에 의존하는 것 외에도.
Adam Barnes

2

할 수있는 것은 변수를 사용하여 상태를 저장하고 수동으로 상태를 해당 변수로 설정 한 다음 해당 변수를 확인하는 것입니다.

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

물론 이것은 약속의 원본 코드에 액세스해야 함을 의미합니다. 그렇지 않은 경우 다음을 수행 할 수 있습니다.

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

내 솔루션은 더 많은 코딩이지만, 사용하는 모든 약속에 대해이 작업을 수행하지 않아도 될 것입니다.



2

Promise.prototype에 메서드를 추가 할 수 있습니다. 다음과 같이 보입니다 :

편집 : 여기에있는 대부분의 답변과 같이 첫 번째 솔루션이 제대로 작동하지 않습니다. 비동기 함수 ".then"이 호출 될 때까지 "보류 중"을 반환하며 이는 즉시 발생하지 않습니다. Promise.race를 사용하는 솔루션에 대해서도 마찬가지입니다. 내 두 번째 솔루션은이 문제를 해결합니다.

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

모든 약속에서 사용할 수 있습니다. 예를 들어:

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

두 번째 (올바른) 해결책 :

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

그리고 그것을 사용하십시오 :

참고 :이 솔루션에서는 "새"연산자를 사용할 필요가 없습니다.

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

1

보다 포괄적 인 es6 버전의 QueryablePromise는 첫 번째 해결 후 체인을 잡고 붙잡을 수 있으며 API를 기본 Promise와 일관성있게 유지하기 위해 즉시 해결하거나 거부 할 수있는 기능을 제공합니다.

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

await관용적 프로토 타이핑과 함께 @ jib 's answer 사용법 .

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

이 비동기 함수는 동기화 된 함수처럼 "거의"즉시 실행됩니다 (또는 실제로는 즉시있을 수도 있음).


1

2019 년

내가 아는 것처럼 간단한 방법은 thenable약속 또는 비동기 작업 주위의 초박형 래퍼입니다.

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

extendPromise 클래스를 사용하여 쿼리 가능한 새 Promise 클래스를 만들 수 있습니다 .

약속 객체의 상태를 동 기적 으로 쿼리하는 데 사용할 수 있는 속성 이있는 인스턴스를 QueryablePromise기본적으로 사용 가능한 Promise클래스 에서 상속 하여 자체 하위 클래스를 만들 수 있습니다 . 그것의 구현은 아래에서 볼 수 있거나 더 나은 설명을 위해 이것을 참조하십시오 .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)


불행히도 기존 API는이 새 클래스를 반환하지 않습니다. 사람들이 이것을 어떻게 사용한다고 상상하십니까?
jib

@jib 답변 해 주셔서 감사합니다. API가이 클래스를 리턴하지 않는다는 것은 무엇을 의미합니까? :(
UtkarshPramodGupta

기존 API는 API를 반환하기 위해 작성해야하므로 API를 반환 하지 않습니다 . 예를 들어 전화 fetch하면 기본 약속을 반환합니다. 수업이 어떻게 도움이 되나요?
jib

글쎄, 우리는 다음과 같이 새로운 QuerablePromise에서 그 페치 호출을 랩핑 할 수 없다 const queryableFetch = new QueryablePromise((resolve, reject) => {fetch(/.../).then((data) => resolve(data)) }). 아니면 문제가 있습니까? : /
UtkarshPramodGupta

그것은 작동해야합니다 , err => reject(err). 두 번째 인수로 잊지 마십시오. 그렇지 않으면 약속 생성자 anti-pattern으로then 간주되는 이유 중 오류를 올바르게 전파하지 않습니다 . 비록 실제로 동기화되어 있지는 않지만 (예 : 이미 해결 된 약속을 감지하지 못함) 발신자를 제어하지 않고 응답이 즉시 필요한 경우에 유용 할 수 있습니다.
jib

1

전체 객체를 문자열로 변환하고 다음과 같은 inspect를 사용하여 약속이 여전히 보류 중인지 확인하는 또 다른 우아 하고 해킹 방법이 util.inspect(myPromise).includes("pending")있습니다.

Node.js에서 테스트 8,9,10,11,12,13

다음은 전체 예입니다

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

결과:

true
true
false
false
false

0

ES7 실험을 사용하는 경우 async를 사용하여 듣고 싶은 약속을 쉽게 감쌀 수 있습니다.

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

나는 약간의 npm 패키지, promise-value를 작성하여 promise 래퍼에 resolved플래그 를 제공합니다 .

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

또한 약속 값 (또는 오류)에 대한 동기 액세스를 제공합니다. 이것은 확장 패턴이 아니라 랩을 따라 Promise 오브젝트 자체를 변경하지 않습니다.


0

이것은 오래된 질문이지만 비슷한 것을하려고했습니다. 나는 n 명의 노동자를 계속 유지해야합니다. 그들은 약속으로 구성되어 있습니다. 스캔하여 해결, 거부 또는 보류 중인지 확인해야합니다. 해결되면 값이 필요합니다. 거부되면 문제를 해결하거나 보류중인 작업을 수행하십시오. 해결되거나 거부되면 계속 진행하기 위해 다른 작업을 시작해야합니다. Promise.all 또는 Promise.race를 사용하여 약속을 배열로 유지하면서 삭제할 수있는 방법을 찾을 수 없습니다. 트릭을 수행하는 작업자를 만듭니다.

필요에 따라 해결하거나 거부하는 약속을 반환하는 약속 생성기 함수가 필요합니다. 약속이 무엇을하는지 알 수 있도록 프레임 워크를 설정하는 함수에 의해 호출됩니다.

아래 코드에서 생성기는 단순히 setTimeout을 기반으로 한 약속을 반환합니다.

여기있어

//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는 promise와 해당 상태 및 반환 된 값이 포함 된 객체를 반환합니다.

다음 코드는 상태를 테스트하고 실행중인 작업자 3 명을 유지하기 위해 새 작업자를 만드는 루프를 실행합니다.

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에서 테스트

BTW이 답변에는 그다지 많지 않지만 비슷한 주제에 관한 다른 사람들에게는 누군가가 "당신이 이해하지 못함"또는 "그게 작동하지 않는 방법"이라고 말할 때 나는 그것을 싫어합니다. 더 나은 방법을 제안하는 것이 좋습니다. 약속이 어떻게 작동하는지에 대한 환자의 설명도 좋습니다.


-1

이 솔루션이 간단하고 기본 약속을 계속 사용할 수 있지만 유용한 동기 검사를 추가 할 수 있다는 것을 알았습니다. 또한 전체 약속 라이브러리를 가져올 필요가 없었습니다.

주의 사항 : 이것은 동기 실행 구문을 검사하기 전에 약속을 실행할 수 있도록 현재 실행 스레드에 일종의 중단이있는 경우에만 작동합니다. 그것은 내가 처음 생각했던 것보다 훨씬 제한된 유용성을 만듭니다. 그러나 여전히 유스 케이스에 유용합니다 ( 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);

https://ourcodeworld.com/articles/read/317/how-to-check-if-a-javascript-promise-has-been-fulfilled-rejected-or-resolved 에서 답을 기반으로 하는 방법이 있습니까? ES6 약속이 이행 / 거부 / 해결되었는지를 알려주시겠습니까?


내 대답에 대한 귀하의 의견에 추가 된 바와 같이-이것은 완전히 잘못되었습니다 : 약속의 상태를 동 기적으로 검사 할 수 없습니다-예를 들어 MakeQueryablePromise(Promise.resolve(3)).isResolved거짓이지만 약속은 분명히 해결되었습니다. 대답은 또한 "해결됨"및 "완료 됨"이라는 용어를 잘못 사용하고 있습니다. 그렇게하려면 .then처리기를 직접 추가하면 동기 검사의 요점을 완전히 놓칠 수 있습니다.
Benjamin Gruenbaum

나는 당신이 무슨 말을하는지 잘 알고 있습니다. JS의 단일 스레드 특성은 방해가되지 않습니까? 약속이 해결 된 것으로 표시 되려면 현재 실행을 중단해야합니다. let wrappedPromise = MakeQueryablePromise(Promise.resolve(3)); setTimeout(function() {console.log(wrappedPromise.isFulfilled())}, 1);당신이 그렇게하는 한, 이것은 잘 작동합니다. 그러나 이것이 유용하기 위해서는 그 사실을 이해해야합니다. 이 경고로 설명을 업데이트하겠습니다. 또한 함수 명명이 더 좋고 관용적 일 수 있다는 데 동의합니다.
Akrikos

그러나 그 시점에서 당신은 then원래 약속을 지키고 어쨌든 비동기이기 때문에 동일한 것을 성취 할 수 있습니다 . process.binding('util').getPromiseDetails작동 하는 방법이 있지만 개인 API를 사용하고 있습니다
Benjamin Gruenbaum

그런 다음 항상해야 할 일이 생겨 코드를 이해하기가 훨씬 더 어려워집니다. 특히 내가 관심을 갖는 것은 약속이 거절되었는지 아닌지에 대한 것입니다. 그래서 제 선택은 그 상태를 다른 곳에 저장하거나 이와 같은 것을하는 것입니다. 나는 내 자신을 게시하기 전에 다른 솔루션을 완전히 읽지 않았다는 것을 인정한다. 이 문제는 처음에 생각했던 것보다 더 끈적합니다.
Akrikos
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.