1.Promise本质击鼓传花的游戏
2.Promise四式击鼓
3.Promise击鼓传花
4.Promise花落谁家知多少
记录学习成果,以便温故而知新
先看一下MDN的描述。
Promise.all() MDN的说明:
Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。
Promise.any() MDN的说明:
Promise.any() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝。
Promise.race() MDN的说明:
Promise.race() 静态方法接受一个 promise 可迭代对象作为输入,并返回一个 Promise。这个返回的 promise 会随着第一个 promise 的敲定而敲定。
Promise.allSettled() MDN的说明:
Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。
MDN说得很明白,四个方法都是静态方法,入参是一个 Promise 可迭代对象,返回是一个单独的 Promise。它们是 promise 并发方法。
本专题叫“花落谁家知多少”,其中“花落谁家”是指Promise对象敲定,进一步是兑现还是拒绝;“知多少”是数量的概念,如所有都兑现,第一个拒绝,第一个对象,任何一个敲定,所有敲定这样的概念。
Promise.all(iterable)
参数说明:
iterable是 一个可迭代对象,例如 Array 或 String。
返回值:
- 已兑现(already fulfilled),如果传入的 iterable 为空。
- 异步兑现(asynchronously fulfilled),如果给定的 iterable 中所有的 promise 都已兑现。兑现值是一个数组,其元素顺序与传入的 promise 一致,而非按照兑现的时间顺序排列。如果传入的 iterable 是一个非空但不包含待定的(pending)promise,则返回的 promise 依然是异步兑现,而非同步兑现。
- 异步拒绝(asynchronously rejected),如果给定的 iterable 中的任意 promise 被拒绝。拒绝原因是第一个拒绝的 promise 的拒绝原因。
硬道理上。
const p1 = Promise.resolve(1);
const p2 = 2;
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("魏紫");
}, 100);
});
Promise.all([p1, p2, p3]).then((values) => {
console.log(values);
});
MDN说:
Promise.all 等待所有兑现(或第一个拒绝)的结果。
这个说得比较中肯,比之前的长篇大论要简洁明了。
// 所有的值都不是 Promise,因此返回的 Promise 将被兑现
const p1 = Promise.all([1, 2, 3]);
// 输入中唯一的 Promise 已经被兑现,因此返回的 Promise 将被兑现
const p2 = Promise.all([4, 5, 6, Promise.resolve(7)]);
// 一个(也是唯一的一个)输入 Promise 被拒绝,因此返回的 Promise 将被拒绝
const p3 = Promise.all([8, 9, 10, Promise.reject(11)]);
console.log(p1);
console.log(p2);
console.log(p3);
// 使用 setTimeout,我们可以在队列为空后执行代码
setTimeout(() => {
console.log("队列现在为空");
console.log(p1);
console.log(p2);
console.log(p3);
});
这段代码终于有了拒绝了。
再回到当初的代码,当初想要获取返回就是考虑到可能会一次获取多张图片。
html2canvas(this.$refs.imgBox, {
height: this.$refs.imgBox.scrollHeight,
width: this.$refs.imgBox.scrollWidth,
}).then((canvas) => {
canvas.toDataURL("image/png")
});
现在再模拟解决一下:
//主调
function test(){
const p1 = Promise.all([getImg(), getImg(), getImg()]);
console.log(p1);
setTimeout(() => {
console.log("队列现在为空");
console.log(p1);
});
}
function getImg(){
return new Promise((resolve, reject) => {
setTimeout(() => { resolve("魏紫") }, 1000);
}).then(() => { return "image/png"; });
}
const p1 = Promise.all([]);
console.log(p1);
Promise.any(iterable)
参数说明:
一个 promise 的可迭代对象(例如一个数组)。
返回:
- 已拒绝(already rejected),如果传入的 iterable 为空的话。
- 异步兑现(asynchronously filfilled),当给定的 iterable 中的任何一个 Promise 被兑现时,返回的 Promise 就会被兑现。其兑现值是第一个兑现的 Promise 的兑现值。
- 异步拒绝(asynchronously rejected),当给定的 iterable 中的所有 Promise 都被拒绝时。拒绝原因是一个 AggregateError,其 errors 属性包含一个拒绝原因数组。无论完成顺序如何,这些错误都是按照传入的 Promise 的顺序排序。如果传递的 iterable 是非空的,但不包含待定的 Promise,则返回的 Promise 仍然是异步拒绝的(而不是同步拒绝的)。
硬道理上。
const pErr = new Promise((resolve, reject) => {
reject("总是失败");
});
const pSlow = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "最终完成");
});
const pFast = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "很快完成");
});
Promise.any([pErr, pSlow, pFast]).then((value) => {
console.log(value);
// pFast 第一个兑现
});
Promise.any() 会以第一个兑现的 Promise 来兑现,即使有 Promise 先被拒绝。
const failure = new Promise((resolve, reject) => {
reject("总是失败");
});
Promise.any([failure, Promise.reject("姚黄")]).catch((err) => {
console.log(err);
});
如果没有 Promise 被兑现,Promise.any() 将使用 AggregateError 进行拒绝。
竟然是以抛异常这种激烈手段拒绝的,貌似没有找到errors属性。
Promise.race(iterable)
参数说明:
iterable是一个 promise 可迭代对象(例如数组)。
返回值:
一个 Promise,会以 iterable 中第一个敲定的 promise 的状态异步敲定。换句话说,如果第一个敲定的 promise 被兑现,那么返回的 promise 也会被兑现;如果第一个敲定的 promise 被拒绝,那么返回的 promise 也会被拒绝。如果传入的 iterable 为空,返回的 promise 就会一直保持待定状态。如果传入的 iterable 非空但其中没有任何一个 promise 是待定状态,返回的 promise 仍会异步敲定(而不是同步敲定)。
硬道理上。
function sleep(time, value, state) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (state === "兑现") {
return resolve(value);
} else {
return reject(new Error(value));
}
}, time);
});
}
const p1 = sleep(500, "一", "兑现");
const p2 = sleep(100, "二", "兑现");
Promise.race([p1, p2]).then((value) => {
console.log(value); // “二”
// 两个都会兑现,但 p2 更快
});
const p3 = sleep(100, "三", "兑现");
const p4 = sleep(500, "四", "拒绝");
Promise.race([p3, p4]).then(
(value) => {
console.log(value); // “三”
// p3 更快,所以它兑现
},
(error) => {
// 不会被调用
}
);
const p5 = sleep(500, "五", "兑现");
const p6 = sleep(100, "六", "拒绝");
Promise.race([p5, p6]).then(
(value) => {
// 不会被调用
},
(error) => {
console.error(error.message); // “六”
// p6 更快,所以它拒绝
}
);
// 传入一个已经解决的 Promise 数组,以尽快触发 Promise.race。
const resolvedPromisesArray = [Promise.resolve("魏紫1"), Promise.resolve("魏紫2")];
const p = Promise.race(resolvedPromisesArray);
// 立即打印 p 的值
console.log(p);
// 使用 setTimeout,我们可以在堆栈为空后执行代码
setTimeout(() => {
console.log("堆栈现在为空");
console.log(p);
});
const foreverPendingPromise = Promise.race([]);
console.log(foreverPendingPromise);
setTimeout(() => {
console.log("堆栈现在为空");
console.log(foreverPendingPromise);
});
一个空的可迭代对象会导致返回的 Promise 一直处于待定状态。
const foreverPendingPromise = Promise.race([]);
const alreadyFulfilledProm = Promise.resolve(100);
const arr = [foreverPendingPromise, alreadyFulfilledProm, "非 Promise 值"];
const arr2 = [foreverPendingPromise, "非 Promise 值", Promise.resolve(100)];
const p = Promise.race(arr);
const p2 = Promise.race(arr2);
console.log(p);
console.log(p2);
setTimeout(() => {
console.log("堆栈现在为空");
console.log(p);
console.log(p2);
});
const data = Promise.race([
fetch("/api"),
new Promise((resolve, reject) => {
// 5 秒后拒绝
setTimeout(() => reject(new Error("请求超时")), 5000);
}),
])
.then((res) => res.json())
.catch((err) => displayError(err));
这段代码倒是很有借鉴意义。
Promise.allSettled(iterable)
参数说明:
iterable是一个以 promise 组成的可迭代对象(例如 Array)对象。
返回值:
- 已兑现(already fulfilled),如果传入的 iterable 为空的话。
- 异步兑现(asynchronously fulfill),当给定的 iterable 中所有 promise 已经敲定时(要么已兑现,要么已拒绝)。兑现的值是一个对象数组,其中的对象按照 iterable 中传递的 promise 的顺序,描述每一个 promise 的结果,无论完成顺序如何。
硬道理上。
Promise.allSettled([
Promise.resolve(33),
new Promise((resolve) => setTimeout(() => resolve(66), 0)),
99,
Promise.reject(new Error("一个错误")),
]).then((values) => console.log(values));
每个结果对象都有以下的属性:
status:一个字符串,要么是 “fulfilled”,要么是 “rejected”,表示 promise 的最终状态。
value:仅当 status 为 “fulfilled”,才存在。promise 兑现的值。
reason:仅当 status 为 “rejected”,才存在,promsie 拒绝的原因。
const p = Promise.allSettled([]);
console.log(p);
setTimeout(() => {
console.log("堆栈现在为空");
console.log(p);
});
到此整个Promise系列就结束了,现在给整个系列做个总结:四击、三传与四知。