如何同步确定 JavaScript Promise 的状态?

IT技术 javascript promise es6-promise
2021-02-06 22:53:05

我有一个纯 JavaScript Promise(内置实现或 poly-fill):

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

根据规范,Promise 可以是以下之一:

  • “已解决”和“已解决”
  • “已解决”和“已拒绝”
  • '待办的'

我有一个用例,我希望同步询问 Promise 并确定:

  • Promise 解决了吗?

  • 如果是这样,Promise 是否已解决?

我知道我可以#then()用来安排在 Promise 更改状态后异步执行的工作。我不是在问如何做到这一点。

这个问题特别是关于Promise 状态的同步询问我怎样才能做到这一点?

6个回答

本机 JavaScript Promise不存在此类同步检查 API。使用本机Promise是不可能做到这一点的。规范没有指定这样的方法。

Userland 库可以做到这一点,如果您的目标是特定引擎(例如 v8)并且可以访问平台代码(也就是说,您可以在core 中编写代码),那么您可以使用特定工具(例如私有符号)来实现这一点. 虽然这是超级具体的,而不是在用户领域。

@Akrikos 那个答案不会让你同步检查Promise的状态 - 例如MakeQueryablePromise(Promise.resolve(3)).isResolved是假的,但Promise很明显已经解决。更不用说答案还错误地使用了“已解决”和“已实现”一词。要做到这一点,您是否可以.then自己添加一个处理程序 - 这完全错过了同步检查的重点。
2021-03-15 22:53:05
@ user619271 有很多方法可以调试Promise。仅仅因为它们不能以您想要调试的方式进行调试并不会使它们变得不切实际。我多年来一直在使用 Promise 并且从未需要同步获取 Promise 的状态。所以“我们必须抛弃原生Promise”的说法是废话。
2021-03-18 22:53:05
所以我们必须扔掉原生的 promise,因为它们不切实际并且总是使用 bluebird。好消息!我如何建议原生Promise被弃用并被排除在节点引擎之外?
2021-03-25 22:53:05
注意:老实说,我相信同步检查的用例很少而且非常罕见,如果您在一个新问题中分享您的具体用例询问如何在没有同步检查的情况下实现它 - 如果有人不会,我会回答它打败我:)
2021-03-26 22:53:05
即使用例很少,包括这样的东西会有什么危害?我需要像这样进行状态检查,以查看上一份工作是否已完成以及我是否可以请求另一份工作。而且我不能只设置一个外部变量,因为该对象有可能在没有通知的情况下更改所有者。更令人恼火的是,我可以看到 Node.js 可以访问这些信息,因为它在我检查时向我展示了它,但是除了解析字符串之外没有其他方法可以获取它??
2021-04-11 22:53:05

不,没有同步 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

+1 来这里正是为了寻找这个。没有回答原始问题的答案的完美示例,但对很多登陆这里的人仍然非常有用。
2021-03-26 22:53:05
谢谢@Matthijs!我已经简化了我的答案。
2021-04-01 22:53:05
这种结构背后有什么具体的原因吗?这对我来说似乎不必要地复杂。据我所知,它的工作原理相同: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 挂起就持续存在的额外Promise。
2021-04-04 22:53:05

在此处输入图片说明

promise-status-async可以解决问题。它是异步的,但它不then用于等待Promise得到解决。

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;
}
OP询问了如何同步进行
2021-03-13 22:53:05
重要的是使用纯函数没有任何伪像和我的Promise的副作用,这非常好 <3
2021-03-28 22:53:05
解决方案非常简单,我的Promise没有任何额外的样板代码。所以这不是对 OP 的完全回答,但仍然是最好的解决方法。
2021-04-01 22:53:05
@Klesun 考虑到已经接受的答案,对于更多人来说,这可能是一个足够好的解决方案,而不仅仅是 OP?
2021-04-03 22:53:05
但是......但是还有其他问题没有明确否认异步解决方案,比如这个(自然关闭,因为没有办法类似的听起来问题实际上可能有不同的条件,对,mods ^_^)
2021-04-10 22:53:05

你可以用 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

以及使用 firefox 测试的结果(chrome 保持顺序)

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

而不是'a value that p should not return',使用符号
2021-03-18 22:53:05
@programmer5000 有什么好处?
2021-04-01 22:53:05
@MoritzSchmitzv.Hülst aSymbol将是一个唯一值,因此您永远不必猜测“值 [...] p 不应返回什么”。但是,对特定对象的引用也能正常工作。
2021-04-09 22:53:05

在提供本机方法之前,您可以在 Node.js 中使用(丑陋的)hack:

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
}
@Tustin2121 对于某些版本,它会因类似Promise.resolve('<pending>').
2021-03-12 22:53:05
@JohnWeisz 可怕的是缺乏向后兼容性。我正在尝试将一个有保证的 API 集成到一个假设一切都是同步的代码库中。它要么做一些可怕的事情,要么重写大量代码。无论哪种方式,我都在犯下暴行。
2021-03-17 22:53:05
只是使用 process.binding('util').getPromiseDetails
2021-03-20 22:53:05
我把它归结为一个 polyfill: Promise.prototype.isPending = function(){ return util.inspect(this).indexOf("<pending>")>-1; }
2021-03-27 22:53:05
那太可怕了
2021-04-04 22:53:05