Как я могу синхронно определить состояние обещания JavaScript?

У меня есть чистое обещание JavaScript (встроенная реализация или поли-заполнение):

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

Согласно спецификации, Promise может быть одним из :

  • «решено» и «решено»
  • «решено» и «отказано»
  • 'в ожидании'

У меня есть вариант использования, когда я хочу синхронно опросить Promise и определить:

  • Обещание улажено?

  • если да, то обещание разрешено?

Я знаю, что могу использовать #then() для планирования работы, которая будет выполняться асинхронно после изменения состояния Promise. Я НЕ спрашиваю, как это сделать.

Этот вопрос конкретно касается синхронного опроса состояния Promise. Как я могу этого добиться?


person jokeyrhyme    schedule 01.06.2015    source источник
comment
установите свойство обещания, которое можно увидеть снаружи, и используйте then() для изменения свойства.   -  person dandavis    schedule 01.06.2015
comment
@jokeyrhyme fwiw, источник v8 code.google.com/p/v8/source/browse/branches/bleeding_edge/src/ см. var promiseStatus = NEW_PRIVATE("Promise#status"); , PromiseSet функцию в SET_PRIVATE(promise, promiseStatus, status);   -  person guest271314    schedule 01.06.2015
comment
Итак, начнем: esdiscuss.org/ тема/   -  person jokeyrhyme    schedule 02.06.2015
comment
Кажется странным, что если вы сделаете const a = Promise.resolve('baz'); console.log(а); и посмотрите в консоли Chrome, вы увидите Promise {[[PromiseStatus]]: разрешено, [[PromiseValue]]: baz} proto : Promise [[PromiseStatus]] : разрешено [[PromiseValue]] : baz, и люди утверждают, что это невозможно быть сделано. Как Хром это делает? (делал это в Plunker с Angular plnkr.co/edit/IPIWgLJKQStI5ubXmcsF   -  person JGFMK    schedule 14.08.2017
comment
Использование node v11.12.0 console.log покажет состояние обещания. НАПРИМЕР. console.log(Promise.new((resolve, reject) => {}) =› Promise { <pending> }   -  person Puhlze    schedule 17.05.2019


Ответы (26)


Для нативных промисов JavaScript такого API синхронной проверки не существует. Это невозможно сделать с нативными промисами. Спецификация не определяет такой метод.

Пользовательские библиотеки могут сделать это, и если вы ориентируетесь на определенный движок (например, v8) и имеете доступ к коду платформы (то есть вы можете писать код в ядре) затем вы можете использовать специальные инструменты (например, частные символы) для достижения этой цели. Это очень специфично, а не в пользовательской среде.

person Benjamin Gruenbaum    schedule 01.06.2015
comment
Примечание. Я искренне считаю, что варианты использования для синхронной проверки немногочисленны и очень редки, если вы поделитесь своим конкретным вариантом использования в новом вопросе и спросите, как добиться этого без синхронной проверки - я отвечу на него. выстрел, если кто-то не опередит меня :) - person Benjamin Gruenbaum; 01.06.2015
comment
Даже если варианты использования редки, какой вред может принести включение чего-то подобного? Мне понадобится подобная проверка состояния, чтобы узнать, завершена ли предыдущая работа и могу ли я запросить другую работу. И я не могу просто установить внешнюю переменную, потому что у объекта может измениться владелец без предварительного уведомления. Что больше раздражает, так это то, что я вижу, что Node.js имеет доступ к этой информации, потому что он показывает ее мне, когда я ее проверяю, но нет другого способа получить ее, кроме разбора строк?? - person Tustin2121; 16.05.2016
comment
@ Tustin2121 Tustin2121, если вы используете Node, вы можете просто использовать bluebird и получить гораздо более богатый API с синхронной проверкой. - person Benjamin Gruenbaum; 17.05.2016
comment
Поэтому мы должны отказаться от нативных промисов, поскольку они непрактичны, и всегда использовать bluebird. Отличные новости! Как сделать так, чтобы нативные промисы устарели и были удалены из движка узла? - person user619271; 04.12.2016
comment
@BenjaminGruenbaum, а что не так с Promise.race? - person Pacerier; 15.10.2017
comment
Многое, мы должны были указать .any вместо этого и сделать ошибку, потому что Марк настаивал. Во-первых, Promise.race([]) — это вечно ожидающее обещание (а не ошибка), обычно вам нужно первое успешное обещание, а не только первое обещание. В любом случае, это не совсем относится к заданному вопросу - ОП спрашивал о синхронной проверке, а не о .race и ее многочисленных недостатках. - person Benjamin Gruenbaum; 16.10.2017
comment
@ user619271 Вы говорите, что нативные промисы непрактичны, потому что в них отсутствует синхронная проверка? - person JLRishe; 30.10.2017
comment
@JLRishe Да. Все, в чем отсутствуют базовые параметры отладки, непрактично, если только это не ассемблерный код. - person user619271; 30.10.2017
comment
@user619271 user619271 Существует множество способов отладки промисов. Тот факт, что их нельзя отладить так, как вы хотите, не делает их непрактичными. Я много лет работаю с промисами, и у меня никогда не было необходимости синхронно получать состояние промиса. Таким образом, утверждение, что мы должны отказаться от нативных промисов, — чепуха. - person JLRishe; 30.10.2017
comment
@JLRishe, пожалуйста, обсудите это в чате. (кстати, промисы можно синхронно проверять в отладчике). - person Benjamin Gruenbaum; 30.10.2017
comment
Хотя полезно знать фактическую спецификацию, этот ответ НИЧЕГО не указывает людям на полезное решение, кроме как использовать эту библиотеку. Конечно, эта библиотека полезна, если вы хотите ее использовать, но stackoverflow.com/questions/21485545/ имеет решение, которое работает без необходимости добавлять библиотека. - person Akrikos; 10.08.2018
comment
@Akrikos, этот ответ не позволяет вам синхронно проверять состояние обещания - например, MakeQueryablePromise(Promise.resolve(3)).isResolved ложно, но обещание совершенно очевидно разрешено. Не говоря уже о том, что в ответе также неправильно используется термин «решено и выполнено». Чтобы сделать это, вы можете просто добавить обработчик .then самостоятельно, что полностью упускает из виду синхронную проверку. - person Benjamin Gruenbaum; 10.08.2018

Нет, 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

person jib    schedule 05.03.2016
comment
Есть ли конкретная причина для этой конструкции? Мне это кажется излишне сложным. Насколько я могу судить, это работает идентично: 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 находится в ожидании. - person Matthijs; 12.06.2017
comment
Спасибо @Mattijs! Я упростил свой ответ. - person jib; 26.08.2017
comment
+1 Зашел именно за этим. Прекрасный пример ответа, не отвечающего на исходный вопрос, но все же очень полезного для многих людей, приземляющихся здесь. - person panepeter; 09.07.2021

введите здесь описание изображения

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;
}
person 0xaB    schedule 15.11.2018
comment
ОП спросил, как сделать это синхронно, хотя - person Klesun; 31.05.2019
comment
@Klesun, может быть, это достаточно хорошее решение для большего количества людей, чем просто ОП, учитывая, что ответ уже принят? - person Isaac; 15.12.2020
comment
Но... но есть и другие вопросы, которые явно не отрицают асинхронные решения, например этот (естественно закрытый, потому что похожее звучание проблемы никак не может иметь другие условия, верно, моды ^_^) - person Klesun; 15.12.2020

Вы можете устроить гонку с помощью 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');

результаты с задержкой (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

person Steween    schedule 01.08.2015
comment
Вместо 'a value that p should not return' используйте символ - person ; 19.05.2017
comment
@programmer5000 В чем преимущество? - person Moritz Schmitz v. Hülst; 05.10.2018
comment
@MoritzSchmitzv.Hülst Symbol будет уникальным значением, поэтому вам никогда не придется угадывать, какое значение [...] p не должно возвращаться. Однако ссылка на конкретный объект будет работать так же хорошо. - person Scott Rudiger; 13.10.2018

Вы можете использовать (уродливый) хак в 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
}
person rabbitco    schedule 07.03.2016
comment
Я свел это к полифиллу: Promise.prototype.isPending = function(){ return util.inspect(this).indexOf("<pending>")>-1; } - person Tustin2121; 16.05.2016
comment
Это ужасно. - person John Weisz; 19.01.2017
comment
@JohnWeisz Что ужасно, так это отсутствие обратной совместимости. Я пытаюсь интегрировать многообещающий API в кодовую базу, которая предполагает, что все синхронно. Он либо делает что-то ужасное, либо переписывает огромные куски кода. В любом случае я совершаю злодеяние. - person rath; 03.08.2017
comment
просто используйте process.binding('util').getPromiseDetails - person amara; 30.10.2017
comment
@ Tustin2121 Tustin2121 В некоторых версиях произойдет сбой с чем-то вроде Promise.resolve('<pending>'). - person user202729; 24.01.2020
comment
@amara process.binding('util').getPromiseDetails — это undefined на узле 16! - person Mir-Ismaili; 28.05.2021

в узле скажем внутренний недокументированный 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> ]
person amara    schedule 30.10.2017
comment
я добавил это, потому что этого не было ни в одном из существующих ответов, а для узла это лучший ответ. документацию по нему легко найти на github.com/nodejs/node. - person amara; 30.10.2017
comment
process.binding('util').getPromiseDetails это undefined на узле 16! - person Mir-Ismaili; 28.05.2021

Обновлено: 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"]

Когда я впервые нашел этот ответ, я искал именно этот вариант использования.

person Michael Cole    schedule 05.04.2016

Вы можете обернуть свои обещания таким образом

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;
}
person SpiderPig    schedule 01.06.2015
comment
Это потребует от OP доступа к обещанию на предыдущем этапе цикла обработки событий. Поскольку .then всегда выполняется асинхронно, OP, который хочет проверить обещание в тот же ход, не получит здесь правильный результат. Обратите внимание, что OP специально спросил об синхронной проверке и упомянул, что они уже знают об асинхронной проверке. - person Benjamin Gruenbaum; 01.06.2015
comment
@BenjaminGruenbaum: не появятся ли значения по умолчанию, если это вызовет код на том же ходу? - person dandavis; 01.06.2015
comment
Конечно, вам придется обернуть все свои обещания во время создания. например внутри функций, которые их создают и возвращают. - person SpiderPig; 01.06.2015
comment
Правильно, в этот момент они больше не являются нативными промисами, вы можете также расширить их так, как они предназначены для расширения с помощью подклассов, что позволит вам делать это элегантно, вместо того, чтобы обезьяны исправляли свойства объекта. - person Benjamin Gruenbaum; 01.06.2015
comment
Независимо от того, расширяете ли вы обещание так, как я показал, или путем создания подклассов, в каждом случае вам все равно придется добавлять свою собственную версию then и catch. - person SpiderPig; 01.06.2015
comment
Правильно, в этот момент вы больше не используете собственные обещания, а свои собственные версии функций, которые делают обещание обещанием, поэтому вы можете также использовать библиотеку или подкласс. - person Benjamin Gruenbaum; 01.06.2015
comment
Тогда, возможно, вы могли бы показать нам свое элегантное решение. Потому что я не думаю, что создание подклассов (т. е. использование цепочки прототипов) будет более элегантным. - person SpiderPig; 01.06.2015
comment
Это на самом деле довольно рабочее решение для меня. Тем не менее, я буду отмечать другой ответ как ответ, так как они перестают быть чистыми промисами JavaScript, как только вы их утыкаете вот так. - person jokeyrhyme; 01.06.2015

Это действительно очень раздражает, что эта базовая функциональность отсутствует. Если вы используете 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. Используйте API отладки V8

Это тот же трюк, который использует 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 };
};
person Matthijs    schedule 12.06.2017
comment
Очень небезопасно делать process._tickCallback (или даже просто %RunMicrotick) — это случайно сломает что-то в вашем коде. Я отчаянно пытался заставить его работать (в основном для поддельных таймеров в асинхронных функциях), но со стороны Node он никогда не был достаточно стабильным. Я как бы бросил работу над ним. API отладочного зеркала V8 здесь вполне уместен. - person Benjamin Gruenbaum; 10.08.2018
comment
И.. DeprecationWarning: DebugContext has been deprecated and will be removed in a future version. :( Похоже V8 удалил его - person Benjamin Gruenbaum; 10.08.2018
comment
Мы (Node) можем полностью запросить у V8 API или предоставить API для прямого просмотра состояния промиса — если вы откроете проблему на github.com/nodejs/promise-use-cases я с радостью подниму его с V8 - person Benjamin Gruenbaum; 10.08.2018
comment
Комментарий ниже в этом разделе показал, что API уже существует: process.binding('util').getPromiseDetails( promise ) возвращает [ 0, ] для ожидания, [ 1, value ] для выполненного и [ 2, value ] для отклоненного. - person Matthijs; 12.08.2018

await использование для @jib 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"
})();

обратите внимание, что эта асинхронная функция выполняется «почти» немедленно, как синхронизированная функция (или, возможно, мгновенно).

person Valen    schedule 21.05.2019

Вы можете добавить метод в 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
person Moshe    schedule 09.10.2017

Предупреждение: этот метод использует недокументированные внутренние компоненты Node.js и может быть изменен без предупреждения.

В Node вы можете синхронно определять состояние промиса, используя 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
person Scott Rudiger    schedule 13.10.2018
comment
Кажется, не работает изнутри jest (на самом деле это единственное место, где меня это интересует). Функция существует, но всегда возвращает undefined. Как узнать, что не так? - person Adam Barnes; 15.05.2019
comment
Хм, я помню, как это работало в mocha; хотя никогда не пробовал это с jest. Может быть, начать новый вопрос со ссылкой здесь и включить вашу версию Node.js, а также версию jest? - person Scott Rudiger; 16.05.2019
comment
К сожалению, меня это больше не интересует. В основном я пытался проверить работоспособность моего разрешаемого/отклоняемого вручную Promise, который я использовал только для проверки того, что должно происходить, пока ожидается Promise, но я полагал, что пока то, что я написал, работает, тогда нет необходимости проверить, что в дополнение к тому, что зависит от него. - person Adam Barnes; 16.05.2019

Есть еще один элегантный и хакерский способ проверить, ожидает ли еще промис, просто преобразовав весь объект в строку и проверив его с помощью проверки следующим образом: 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

person devio    schedule 05.05.2020
comment
Это интересный хак, но имейте в виду, что util.inspect предназначен для отладки, и на него не следует сильно полагаться, поскольку вывод может измениться в любое время. - person Leonardo Raele; 11.12.2020
comment
Также util.inspect недоступен в браузере (без полифилла). - person Leonardo Raele; 11.12.2020

что вы можете сделать, так это использовать переменную для хранения состояния, вручную установить состояние для этой переменной и проверить эту переменную.

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

Мое решение заключается в большем количестве кода, но я думаю, что вам, вероятно, не придется делать это для каждого обещания, которое вы используете.

person BigName    schedule 08.04.2016

Начиная с Node.js версии 8, теперь вы можете использовать пакет wise-inspection для синхронной проверки родные промисы (без всяких опасных хаков).

person Joshua Wise    schedule 21.08.2017

Вот более конкретизированная версия QueryablePromise для es6, позволяющая сцеплять затем и перехватывать после первого разрешения, а также немедленно разрешать или отклонять, чтобы API соответствовал нативному промису.

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>

person synthet1c    schedule 01.02.2019

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
})
person pery mimon    schedule 28.11.2019

Вы можете extend класс Promise создать новый запрашиваемый класс 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)

person UtkarshPramodGupta    schedule 25.12.2019
comment
К сожалению, ни один из существующих API не вернет этот новый класс. Как вы представляете, как люди используют это? - person jib; 27.12.2019
comment
@jib Спасибо за ваш ответ. Что вы имеете в виду, что ни один API не вернет этот класс? :( - person UtkarshPramodGupta; 27.12.2019
comment
Никакие существующие API не вернут его, потому что они должны быть написаны, чтобы вернуть его, верно? Например. если я вызову fetch, он вернет собственное обещание. Как ваш класс может помочь в этом? - person jib; 27.12.2019
comment
Ну, разве мы не можем просто обернуть этот вызов выборки в наш новый QuerablePromise, например: const queryableFetch = new QueryablePromise((resolve, reject) => {fetch(/.../).then((data) => resolve(data)) }) ? Или в этом проблема? :/ - person UtkarshPramodGupta; 27.12.2019
comment
Это должно работать, просто не забудьте , err => reject(err) в качестве второго аргумента для then, иначе он не будет правильно распространять ошибки (среди причин, по которым он считается анти-шаблоном конструктора промисов). Хотя это не совсем синхронно (например, не обнаружит уже разрешенное обещание), но, возможно, полезно в случаях, когда вы не контролируете вызывающего абонента, а ответ требуется немедленно. - person jib; 27.12.2019
comment
Пожалуйста, прекратите спамить ссылки на этот ответ под контентом других людей. - person meagar; 02.01.2020

Я просмотрел решения, предложенные для этого вопроса, и не нашел ни одного, соответствующего простому подходу, который я использовал в Node.js.

Я определил простой класс PromiseMonitor, который принимает промис в качестве единственного параметра для своего конструктора и имеет строковое свойство .status, которое возвращает стандартные строковые значения, соответствующие статусу промиса: ожидание, разрешение или отклонение, и четыре логических свойства .pending, .resolved, .rejected и .error. Свойство .error устанавливается в значение true, только если .rejected равно true и обратному вызову отклонения был передан объект Error.

Класс просто использует .then() в обещании, чтобы изменить статус PromiseMonitor, когда обещание разрешено или отклонено. Это не мешает любому другому использованию исходного промиса. Вот код:

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

Чтобы отслеживать статус обещания, просто создайте экземпляр PromiseMonitor, передав обещание в качестве параметра, например:

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

Теперь вы можете синхронно проверять все свойства promiseMonitor, которые будут отслеживать статус исходного промиса. Вот тестовый сценарий, демонстрирующий три возможных разрешения отслеживаемого промиса.

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();
person JohnRC    schedule 20.12.2020

ПРЕДОСТЕРЕЖЕНИЕ: process.binding('util').getPromiseDetails равно undefined на узле 16!

Ориентир:

Кандидаты:

/**
 * https://stackoverflow.com/a/47009572/5318303
 */
const isPromisePending1 = (() => { // noinspection JSUnresolvedFunction
    const util = process.binding('util')  // noinspection JSUnresolvedFunction
    return promise => !util.getPromiseDetails(promise)[0]
})()

/**
 * https://stackoverflow.com/a/35852666/5318303
 */
const isPromisePending2 = (promise) => util.inspect(promise) === 'Promise { <pending> }'

/**
 * https://stackoverflow.com/a/35820220/5318303
 */
const isPromisePending3 = (promise) => {
    const t = {}
    return Promise.race([promise, t])
            .then(v => v === t, () => false)
}

Тестовые обещания:

const a = Promise.resolve()
const b = Promise.reject()
const c = new Promise(() => {})
const x = (async () => 1)()

Запустите тест:

const n = 1000000

console.time('isPromisePending1')
for (let i = 0; i < n; i++) {
    isPromisePending1(a)
    isPromisePending1(b)
    isPromisePending1(c)
    isPromisePending1(x)
}
console.timeEnd('isPromisePending1')

console.time('isPromisePending2')
for (let i = 0; i < n; i++) {
    isPromisePending2(a)
    isPromisePending2(b)
    isPromisePending2(c)
    isPromisePending2(x)
}
console.timeEnd('isPromisePending2')

console.time('isPromisePending3')
for (let i = 0; i < n; i++) {
    await isPromisePending3(a)
    await isPromisePending3(b)
    await isPromisePending3(c)
    await isPromisePending3(x)
}
console.timeEnd('isPromisePending3')

Результат:

isPromisePending1: 440.694ms
isPromisePending2: 3.354s
isPromisePending3: 4.761s

Очевидно, что isPromisePending1() работает слишком быстро (в 8~10 раз)! Но его нельзя использовать на узле 16! (см. предостережение выше).

person Mir-Ismaili    schedule 25.05.2021

Если вы используете экспериментальный ES7, вы можете использовать асинхронность, чтобы легко обернуть обещание, которое вы хотите прослушать.

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;
}
person Ezequiel S. Pereira    schedule 11.10.2016

Я написал небольшой пакет npm, promise-value, который предоставляет оболочку обещания с флагом resolved:

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

Это также дает синхронный доступ к значению обещания (или ошибке). Это не изменяет сам объект Promise, следуя шаблону переноса, а не расширения.

person Daniel Winterstein    schedule 29.10.2017

Это старый вопрос, но я пытался сделать что-то подобное. Мне нужно сохранить 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 возвращает объект, содержащий обещание, его состояние и возвращаемое значение.

Следующий код запускает цикл, который проверяет состояние и создает новых воркеров, чтобы поддерживать его на уровне 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

Кстати, не столько в этом ответе, сколько в других на похожие темы, я НЕНАВИЖУ, когда кто-то говорит «вы не понимаете» или «это не так». Я обычно предполагаю, что спрашивающий знает, чего хочет. Предложить лучший способ — это здорово. Также было бы хорошо терпеливое объяснение того, как работают промисы.

person Charles Bisbee    schedule 11.02.2018

Старый вопрос со многими ответами, но ни один из них, похоже, не предлагает того, что я считаю самым простым решением: установить логический индикатор на разрешение/отклонение обещания.

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

person junvar    schedule 06.08.2020
comment
Это неправильный ответ, потому что он подразумевает, что у вас есть доступ к реализации обратного вызова Promise, что, скорее всего, не так, иначе да, это было бы так же просто, как установить флаг. - person Leonardo Raele; 11.12.2020

Это шаблон Future, который я использую: (https://github.com/Smallscript-Corp)

  • включает использование sync и async fn
  • позволяет унифицировать шаблоны событий с async поведением
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

Затем вы можете создавать экземпляры future-value, которые можно разрешить с помощью функций синхронизации и асинхронности; позволяет единообразно обрабатывать события.

Вы можете использовать его для написания шаблона, например:

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))
}
person smallscript    schedule 12.01.2021

Я нашел это решение простым и позволяющим мне продолжать использовать собственные промисы, но добавить полезные синхронные проверки. Мне также не нужно было использовать всю библиотеку промисов.

ПРЕДОСТЕРЕЖЕНИЕ: Это работает только в том случае, если в текущем потоке выполнения есть какой-то перерыв, позволяющий выполнить обещания ДО проверки синхронных конструкций. Это делает его более ограниченным по полезности, чем я изначально думал, но все же полезно для моего варианта использования (спасибо 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?

person Akrikos    schedule 10.08.2018
comment
Как добавлено в вашем комментарии к моему ответу, это совершенно неверно: это не позволяет вам синхронно проверять состояние обещания. Например, MakeQueryablePromise(Promise.resolve(3)).isResolved является ложным, но обещание совершенно очевидно разрешено. Не говоря уже о том, что в ответе также неправильно используется термин «решено и выполнено». Чтобы сделать это, вы можете просто добавить обработчик .then самостоятельно, что полностью упускает из виду синхронную проверку. - person Benjamin Gruenbaum; 10.08.2018
comment
Я вижу, что вы говорите, и вы делаете хорошее замечание. Однопоточная природа JS мешает, не так ли? Вы должны приостановить текущее выполнение, чтобы обещание было помечено как выполненное. let wrappedPromise = MakeQueryablePromise(Promise.resolve(3)); setTimeout(function() {console.log(wrappedPromise.isFulfilled())}, 1); Пока вы это делаете, это прекрасно работает. Но вы должны понимать этот факт, чтобы это было полезно. Я обновлю описание с этой оговоркой. Я также согласен с тем, что имена функций могли бы быть лучше/более идиоматичными. - person Akrikos; 22.08.2018
comment
Но в этот момент вы могли бы просто then выполнить исходное обещание и выполнить то же самое, поскольку оно все равно асинхронно. Есть способ с process.binding('util').getPromiseDetails, который работает, но он использует частный API. - person Benjamin Gruenbaum; 22.08.2018
comment
Это неприятно, когда приходится постоянно это делать, и это делает код намного более трудным для понимания. Особенно, когда меня волнует только то, было ли обещание отклонено или нет, поэтому я могу либо сохранить это состояние где-то еще, либо сделать что-то подобное. Я признаю, что не читал другие решения здесь полностью, прежде чем опубликовать свое собственное - извиняюсь за это. Эта проблема сложнее, чем я думал сначала. - person Akrikos; 22.08.2018