Promise.race
and Promise.any
做不同的事情:
Promise.race
一旦你提供的任何承诺得到解决,无论它们是被履行还是被拒绝,它都会立即得到解决。
Promise.any
一旦您提供任何承诺,就会立即解决已实现或者他们是all被拒绝,在这种情况下,它会被拒绝AggregateError
.
主要区别是:
-
race
当你给它的第一个承诺被拒绝时,它的承诺就被拒绝了;any
的承诺不是,因为另一个承诺可能会被履行。
-
any
的承诺的拒绝原因将是AggregateError
, but race
的拒绝原因将是第一个被拒绝的 Promise 的拒绝原因。
因此,如果您将两个承诺的数组传递给它们,并且其中一个承诺被拒绝,那么随后另一个承诺将得到履行,来自的承诺Promise.race
将被拒绝(因为第一个解决的承诺被拒绝)并且来自Promise.any
将会实现(因为虽然第一个承诺被拒绝,但第二个承诺实现了)。例如。:
const a = new Promise((_, reject) => setTimeout(reject, 100, new Error("a")));
const b = new Promise((resolve) => setTimeout(resolve, 200, "b"));
Promise.race([a, b]).then(
value => {
console.log(`race: fulfilled with ${value}`);
},
reason => {
console.log(`race: rejected with ${reason.message}`);
}
);
Promise.any([a, b]).then(
value => {
console.log(`any: fulfilled with ${value}`);
},
reason => {
console.log(`any: rejected with ${reason.errors.map(({message}) => message).join()}`);
}
);
使用具有以下功能的 JavaScript 引擎Promise.any
(或填充),输出
race: rejected with a
any: fulfilled with b
在这里尝试不同的结果(有一个very粗糙不完整的替代品Promise.any
如果您的浏览器还没有包含它):
addFakeAnyIfMissing();
document.querySelector("input[value='Start Again']").addEventListener("click", run);
run();
function setupPromise(name) {
return new Promise((resolve, reject) => {
const div = document.querySelector(`[data-for="${name}"]`);
const btnFulfill = div.querySelector("input[value=Fulfill]");
const btnReject = div.querySelector("input[value=Reject]");;
const display = div.querySelector(".display");
btnFulfill.disabled = btnReject.disabled = false;
display.textContent = "pending";
btnFulfill.onclick = () => {
resolve(name);
display.textContent = `fulfilled with ${name}`;
btnFulfill.disabled = btnReject.disabled = true;
};
btnReject.onclick = () => {
reject(new Error(name));
display.textContent = `rejected with Error(${name})`;
btnFulfill.disabled = btnReject.disabled = true;
};
});
}
function run() {
const a = setupPromise("a");
const b = setupPromise("b");
const raceDisplay = document.querySelector("[data-for=race] .display");
const anyDisplay = document.querySelector("[data-for=any] .display");
raceDisplay.textContent = anyDisplay.textContent = "pending";
Promise.race([a, b]).then(
value => {
raceDisplay.textContent = `fulfilled with ${value}`;
},
reason => {
raceDisplay.textContent = `rejected with ${reason.message}`;
}
);
Promise.any([a, b]).then(
value => {
anyDisplay.textContent = `fulfilled with ${value}`;
},
reason => {
anyDisplay.textContent = `rejected with ${reason.errors.map(({message}) => message).join()}`;
}
);
}
function addFakeAnyIfMissing() {
if (!Promise.any) {
// VERY ROUGH STANDIN, not a valid polyfill
class AggregateError extends Error {}
Object.defineProperty(Promise, "any", {
value(iterable) {
return new Promise((resolve, reject) => {
const errors = [];
let waitingFor = 0;
for (const value of iterable) {
const index = waitingFor++;
Promise.resolve(value).then(
value => {
resolve(value);
--waitingFor;
},
reason => {
errors[index] = reason;
if (--waitingFor === 0) {
reject(Object.assign(new AggregateError(), {errors}));
}
}
);
}
});
},
writable: true,
configurable: true
});
}
}
<div data-for="a">
Promise A
<input type="button" value="Fulfill">
<input type="button" value="Reject">
<span class="display"></span>
</div>
<div data-for="b">
Promise B
<input type="button" value="Fulfill">
<input type="button" value="Reject">
<span class="display"></span>
</div>
<div data-for="race">
<code>Promise.race([a, b])</code>:
<span class="display"></span>
</div>
<div data-for="any">
<code>Promise.any([a, b])</code>:
<span class="display"></span>
</div>
<input type="button" value="Start Again">
这张图表来自提案 https://github.com/tc39/proposal-promise-any可能有帮助:
有Promise 景观中的四个主要组合器 https://v8.dev/features/promise-combinators.
+−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−+
| name | description | |
+−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−+
| Promise.allSettled | does not short-circuit | Added in ES2020 |
| Promise.all | short-circuits when an input value is rejected | Added in ES2015 |
| Promise.race | short-circuits when an input value is settled | Added in ES2015 |
| Promise.any | short-circuits when an input value is fulfilled | this proposal |
+−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+−−−−−−−−−−−−−−−−−+
继续你的问题...
这种平等的另一个例子是“命运未决的承诺必然是悬而未决的”。 “如果一个承诺没有悬而未决,即如果它要么被履行,要么被拒绝,我们就说这个承诺已经解决。”。
因此,如果一个承诺得到解决,它就不是未解决的,因此它不是悬而未决的。那么,如果没有悬而未决,那就解决了。于是解决===解决了。
我可以看到你是如何到达那里的,但你不能那样颠倒它。 :-) 已解决的承诺可能会悬而未决。这只是一个未解决的承诺是确实待办的。
这些州是:
你可以解决一个承诺(A
)到另一个承诺(B
),这意味着虽然A
可能仍然悬而未决,没有什么可以改变将要发生的事情;它的命运是注定的,它的实现或拒绝取决于所发生的事情B
.
(有关此内容的更多信息,请参阅我的博客文章我们来谈谈如何谈论承诺 https://thenewtoys.dev/blog/2021/02/08/lets-talk-about-how-to-talk-about-promises/.)
以下是待解决的承诺的示例:
const b = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.5) {
resolve("all good");
} else {
reject(new Error("ugh"));
}
}, 100);
});
// (Being verbose for clarity)
const a = new Promise((resolve, reject) => {
resolve(b);
// Now, `a` is pending, but resolved
// No matter what else we do, `a`'s fate is tied to
// `b`'s. For instance, this does nothing:
resolve("foo");
// Neither does this:
reject(new Error("foo"));
});
b
.then(value => {
console.log(`b was fulfilled: ${value}`);
})
.catch(reason => {
console.log(`b was rejected: ${reason.message}`);
});
a
.then(value => {
console.log(`a was fulfilled: ${value}`);
})
.catch(reason => {
console.log(`a was rejected: ${reason.message}`);
});