Promise 是 JavaScript 中的对促发器操作形式最差的 API 众所周知。做为JavaScript合作开发者,须要熟练 Promise 有关的科学知识。责任编辑就来归纳呵呵那些习题,先来简述 JavaScript 往后促发器操作形式的处置形式?接着详尽如是说 Promises 第一类及有关的方式。
那时先来介绍呵呵 JavaScript 促发器基本概念。
促发器简述
期结论,而要今后透过很大的形式获得(比如反弹表达式),这表达式是促发器。
促发器反弹,是常用的 callback ,这是往后在 JavaScript 中处置促发器操作形式的常用形式,如 AJAX ,发动一个HTTP允诺,具体内容伺服器甚么此时候回到积极响应统计数据依赖于顾客的自然环境,存有许多不确认不利因素,此时选用反弹表达式能在回到积极响应统计数据的此时候促发反弹表达式。
此种形式布季谢数个允诺的情形,先期允诺须要等候后面允诺的积极响应统计数据的此时候,就会再次出现常用的反弹冥界。
const asyncMessage = function (message, callback) {
setTimeout(function () {
console.log(message);
callback();
}, 1000);
};
asyncMessage(“title”, function () {
asyncMessage(“cate”, function () {
asyncMessage(“content”, function () {
console.log(“detail”);
});
});
});
再次出现反弹冥界Sonbhadra给工程项目合作开发增添许多难题:
会引致逻辑纷乱,耦合性高,更动两处就会引致全数发生变动,冗余多时,BUG难题无法辨认出。无法选用 try…catch 来捕捉极度。无法选用 return 回到真实世界统计数据为的是防止反弹冥界的再次出现,而后就有了 Promise 第一类。
Promise 的工作形式
promise 第一类是两个能从促发器表达式同步回到的第一类,它将处于 3 种可能的状态众所周知:
fulfilled 已兑现: onFulfilled() 将被调用,即操作完成(比如,resolve() 被调用) rejected 已拒绝: onRejected() 将被调用,即操作形式失败(比如,reject() 被调用) pending 待定:初始状态,既没有被兑现,也没有被拒绝
如果两个 promise 没有挂起(它已被解决或拒绝),它就会被解决。有时选用已解决和已解决表示同一件事:不是 pending。
promise 一旦确认,就无法再重新确认,再次调用 resolve() 或reject() 将没有效果。两个已确认的 promise 的具有不可变性。
对于 promise 的状态监控能选用承诺链,即在 fulfilled 已兑现的状态能选用 then 方
const myPromise = new Promise(myExecutorFunc)
.then(handleFulfilledA)
.then(handleFulfilledB)
.then(handleFulfilledC)
.catch(handleRejectedAny);
看起来比 callback 的形式优雅一点,对于须要发动多次HTTP允诺才能完整呈现的需求,代码如下:
const getPost = () => fetch(“https://jsonplaceholder.typicode.com/posts/1”);
const getAuthor = (id) =>
fetch(“https://jsonplaceholder.typicode.com/users/” + id);
const getComment = (id) =>
fetch(“https://jsonplaceholder.typicode.com/users/” + id);
getPost() // #1.fetch post
.then((postResponse) => postResponse.json()) // #2. get & return post json
.then((postResponse) =>
getAuthor(postResponse.id) // #3. fetch author
.then((authorResponse) =>
authorResponse
.json() // #4 get & return author json
.then((authorResponse) =>
getComment(postResponse.id) // #5 fetch comment
.then((commentResponse) => commentResponse.json()) // #6 get & return comment json
.then((commentResponse) => {
// #7 time to combine all results
return {
postResponse,
authorResponse,
commentResponse,
}; // #8 combine & return all reponses
})
)
)
.then((results) => {
// #9 read all responses
console.log(results.postResponse);
console.log(results.authorResponse);
console.log(results.commentResponse);
})
)
.catch((error) => console.log(error)); // # 10 error handling
上面代码是否有种似曾相识的感觉,原本是为介绍决反弹冥界,但似乎理想跟现实还是有差距。
于是 ES2021 为 Promise 第一类增加新的特征,其中包括:Promise.any()、Promise.all()、Promise.allSettled()、Promise.race()。
Promise.any()
Promise.any(promises) 能够并行运行 promise,并解析为 promises 列表中第两个成功解析的 promise 的值。须要注意的是 Promise.any() 方式依然是实验性的,尚未被所有的浏览器完全支持。
下面来看看 Promise.any() 是如何工作的。
1.工作原理
Promise.any() 可用于以并行和竞争形式执行独立的促发器
该表达式接受两个 promise 数组(通常为两个可迭代第一类)做为参数,如下:
const anyPromise = Promise.any(promises);
当输入 promises 中的第两个 promise 被执行完成时,anyPromise 会立即解析为该 promise 的值。
能选用 then 方式提取第两个 promise 的值:
anyPromise.then((firstValue) => {
firstValue; // 第两个 promise 完成后回到的值
});
也能选用 async/await 语法:
const firstValue = await anyPromise;
console.log(firstValue); // 第两个 promise 完成后回到的值
Promise.any() 回到的 promise 与任何第两个执行的 promise 一起执行。即使某些 promise 被 rejected,那些 rejections 也Sonbhadra被忽略。
但是,如果输入数组中的所有 promises 都被拒绝,或者输入数组为空,那么Promise.any() 会 rejected 包含输入的 promises 执行的 rejection 错误原因集合。
2. 选用指南
那时来深入如是说呵呵 Promise.any(), 在这之前,先来定义 2 个简单的表达式。
表达式 resolveTimeout(value, delay) 将回到两个在经过 delay 时间后有 resolve 的 promise 。function resolveTimeout(value, delay) {
return new Promise((resolve) => setTimeout(() => resolve(value), delay));
}
表达式 rejectTimeout(reason, delay) 将回到两个在经过 delay 时间后有 reject 的 promise 。function rejectTimeout(reason, delay) {
return new Promise((r, reject) => setTimeout(() => reject(reason), delay));
}
接下来选用上面定义的2个辅助表达式来试试 Promise.any() 。
2.1 完成所有 promises
下面尝试运行第两个解析列表:
function resolveTimeout(value, delay) {
return new Promise((resolve) => setTimeout(() => resolve(value), delay));
}
function rejectTimeout(reason, delay) {
return new Promise((r, reject) => setTimeout(() => reject(reason), delay));
}
const fruits = [“potatoes”, “tomatoes”];
const vegetables = [“oranges”, “apples”];
const promise = Promise.any([
resolveTimeout(fruits, 1000),
resolveTimeout(vegetables, 2000),
]);
// 等候…
const list = async () => {
const result =
await promise;
console.log(result);
};
// 1 秒之后
list(); // [potatoes, tomatoes]
promise .any([…]) 回到两个在 1秒内 解析到数组 fruits 的 promise,因为解析fruits的 promise 先执行完成。
第二个是2秒内 解析到数组 vegetables 的 promise,其值将被忽略。
2.2 两个 promise 被 rejected
将上面第两个 promise 再次出现极度被 rejected ,如下代码:
function resolveTimeout(value, delay) {
return new Promise((resolve) => setTimeout(() => resolve(value), delay));
}
function rejectTimeout(reason, delay) {
return new Promise((r, reject) => setTimeout(() => reject(reason), delay));
}
const vegetables = [“oranges”, “apples”];
const promise = Promise.any([
rejectTimeout(new Error(“fruits is empty”), 1000),
resolveTimeout(vegetables, 2000),
]);
// 等候…
const list = async () => {
const result = await promise;
console.log(result);
};
// 2 秒之后
list(); // [ oranges, apples ]
上面的代码,第两个 promise 在 1秒后 被rejected,从执行的结论不难看出 Promise.any() 跳过了第两个被rejected的promise ,等候第二个 2秒后 执行完成的promise。
2.3 所有的 promises 被 rejected
下面来看下当所有的 promises 被 rejected 会再次出现甚么结论,如下代码:
function rejectTimeout(reason, delay) {
return new Promise((r, reject) => setTimeout(() => reject(reason), delay));
}
const promise = Promise.any([
rejectTimeout(new Error(“fruits is empty”), 1000),
rejectTimeout(new Error(“vegetables is empty”), 2000),
]);
// 等候…
const list = async () => {
try {
const result = await promise;
console.log(result);
} catch (aggregateError) {
console.log(aggregateError);
console.log(aggregateError.errors);
}
};
list(); // [AggregateError: All promises were rejected]
从上面代码的执行结论来看,当所有输入promises 被 rejected 后, Promise.any([…]) 将返回一种特殊的错误 AggregateError 而被 rejected ,而详尽的 rejected 原因在属性 aggregateError.errors 中 。
小结
Promise.any() 可用romise 也会以错误集合的形式拒绝,该错误在两个特殊属性 AggregateError 中包含输入 promise 的拒绝原因:aggregateError.errors 。
Promise.all()
方式 Promise.all(promises) ,能够一次并行处置数个 promise,并且只回到两个 promise 实例, 那个输入的所有 promise 的 resolve 反弹的结论是一个数组。
下面来看看 Promise.all() 是如何工作的。
1.工作原理
Promise.all() 是两个内置的辅助表达式,接受一组 promise(或者两个可迭代的第一类),并回到两个promise :
const allPromise = Promise.all([promise1, promise2, …]);
能选用 then 方式提取第两个 promise 的值:
allPromise.then((values) => {
values; // [valueOfPromise1, valueOfPromise2, …]
});
也能选用 async/await 语法:
const values = await allPromise;
console.log(values); // [valueOfPromise1, valueOfPromise2, …]
Promise.all() 回到的 promise 被解析或拒绝的形式。
如果 allPromise 都被成功解析,那么 allPromise 将选用两个包含各个 promise 已执行完成后的值的数组做为结论。数组中 promise 的顺序是很重要的——将按照这个顺序获得已实现的值。
但是如果至少有两个 promise 被 rejected ,那么 allPromise 会以同样的原因立即 rejected (不等候其他 promise 的执行)。
如果所有的 promise 被 rejected ,等候所有的promise 执行完成,但只会回到最先被rejected 的promise 的 reject 原因。
2. 选用指南
那时来深入如是说呵呵 Promise.all(), 在这之前,先来定义 2 个简单的表达式。
表达式 resolveTimeout(value, delay) 将回到两个在经过 delay 时间后有 resolve 的 promise 。function resolveTimeout(value, delay) {
return new Promise((resolve) => setTimeout(() => resolve(value), delay));
}
表达式 rejectTimeout(reason, delay) 将回到两个在经过 delay 时间后有 reject 的 promise 。 function rejectTimeout(reason, delay) {
return new Promise((r, reject) => setTimeout(() => reject(reason), delay));
}
接下来选用上面定义的2个辅助表达式来试试 Promise.all() 。
2.1 完成所有 promises
下面定义了两个 promise 数组 allPromise ,所有的 promise 都能够成功的 resolve 值,如下:
function resolveTimeout(value, delay) {
return new Promise((resolve) => setTimeout(() => resolve(value), delay));
}
const fruits = [“potatoes”, “tomatoes”];
const vegetables = [“oranges”, “apples”];
const allPromise = [
resolveTimeout(fruits, 2000),
resolveTimeout(vegetables, 1000),
];
const promise = Promise.all(allPromise);
// 等候… 2秒后
const list = async () => {
try {
const result = await promise;
console.log(result);
} catch (error) {
console.log(error.errors);
}
};
list(); // [ [ potatoes, tomatoes ], [
oranges, apples ] ]
从上面执行的结论来看 Promise.all() 回到的 promise 的 resolve 数组是按照执行前 allPromise 的顺序组成其结论。
promise 数组的顺序直接影响结论的顺序,和 promise 执行完成的先后无关。2.2 两个 promise 被 rejected
将上面数组 allPromise 的第两个 promise 再次出现极度被 rejected ,如下代码:
const promise = Promise.all([
rejectTimeout(new Error(“fruits is empty”), 5000),
resolveTimeout(vegetables, 1000),
]);
// 等候…
const list = async () => {
try {
const result = await promise;
console.log(result);
} catch (error) {
console.log(error);
}
};
list(); // Error: fruits is empty
然而,在经过 5秒 之后,第两个 promise 由于极度被 rejected ,使得 allPromise 也被 rejected ,并回到跟第两个 promise 一样的错误信息:Error: fruits is empty ,即使在 1秒 后就完成的第二个 promise 的值也不被采纳。
接下来将数组 allPromise 的所有 promise 都抛出极度被 rejected ,透过定时器将 rejected 的顺序做个调整,如下:
const promise = Promise.all([
rejectTimeout(new Error(“fruits is empty”), 5000),
rejectTimeout(new Error(“vegetables is empty”), 1000),
]);
// 等候…
const list = async () => {
try {
const result = await promise;
console.log(result);
} catch (error) {
console.log(error);
}
};
经过 5秒 之后完成执行,而结论显示为 Error: vegetables is empty ,不难看出 allPromise 被 rejected 的原因是最先 rejected 的promise 。
Promise.all() 的此种行为被称为快速失败,如果 promise 数组中至少有两个 promise 被 rejected ,那么回到的 promise 也被拒绝。如果promise 数组中所有的都被 rejected ,那么回到的promise 被拒绝的原因是先rejected的那两个。小结
Promise.allSettled()
方式 Promise.allSettled(promises) ,回到两个在所有给定的 promise 都已经 fulfilled 或 rejected 后的 promise ,并带有两个第一类数组,每个第一类表示对应的promise 结论。
下面来看看 Promise.allSettled() 是如何工作的。
1.工作原理
Promise.allSettled() 可用于并行执行独立的促发器操作形式,并收集那些促发器操作形式的结论。
表达式接受两个 promise 数组(或通常是两个可迭代的)做为参数,如下:
const statusesPromise = Promise.allSettled(promises);
当所有输入 promises 都被履行或拒绝时,statusesPromise 会解析为两个具有其状态的数组:
{ status: fulfilled, value:value } : 如果相应的 promise 已经履行{ status: rejected, reason: reason } :如果相应的 promise 被拒绝
能选用 then 方式提取所有 promises 的状态:
statusesPromise.then((statuses) => {
statuses; // [{ status: …, value: … }, …]
});
也能选用 async/await 语法:
const statuses = await statusesPromise;
statuses; // [{ status: …, value: … }, …]
Promise.allSettled() 回到的承诺总是以一系列状态实现,无论是否有一些(或者全数)输入承诺被拒绝。
Promise.allSettled() 和 Promise.all() 的最大不同:Promise.allSettled() 永远不会被 rejected 。 2. 选用指南
那时来深入如是说 Promise.allSettled() 的选用之前, 还是先来定义 2 个简单的表达式。
function resolveTimeout(value, delay) {
return new Promise((resolve) => setTimeout(() => resolve(value), delay));
}
function rejectTimeout(reason, delay) {
return new Promise((r, reject) => setTimeout(() => reject(reason), delay));
}
接下来选用上面定义的2个辅助表达式来试试 Promise.allSettled() 。
2.1 完成所有 promises
下面定义了两个 promise 数组 statusesPromise ,所有的 promise 都能够成功的 resolve 值,如下:
const fruits = [“potatoes”, “tomatoes”];
const vegetables = [“oranges”, “apples”];
const statusesPromise = Promise.allSettled([
resolveTimeout(fruits, 2000),
resolveTimeout(vegetables, 1000),
]);
// 等候 2 秒 …
const list = async () => {
try {
const statuses = await statusesPromise;
console.log(statuses);
} catch (error) {
console.log(error);
}
};
list(); // [{ status: fulfilled, value: [
potatoes, tomatoes ] },{ status: fulfilled, value: [ oranges, apples ] }]
从上面执行的结论来看 Promise.allSettled() 回到的两个 promise 的 resolve 状态数组是按照执行前 statusesPromise 的顺序组成其结论。
2.2 两个 promise 被 rejected
将上面第两个 promise 再次出现极度被 rejected ,如下代码:
const fruits = [“potatoes”, “tomatoes”];
const statusesPromise = Promise.allSettled([
resolveTimeout(fruits, 2000),
rejectTimeout(new Error(“Vegetables is empty”), 1000),
]);
// 等候 2 秒 …
const list = async () => {
try {
const statuses = await statusesPromise;
console.log(statuses);
} catch (error) {
console.log(error);
}
};
list(); // // [{ status: fulfilled, value: [potatoes, tomatoes] },{ status: rejected, reason: Error(Vegetables is empty) }]
即使输入数组中的第二个 promise 被 rejected , statusesPromise 仍然能成功解析状态数组。
2.3 所有 promises 被 rejected
将上面所有的 promises 再次出现极度被 rejected ,如下代码:
const statusesPromise = Promise.allSettled([
rejectTimeout(new Error(“Fruits is empty”), 2000),
rejectTimeout(new Error(“Vegetables is empty”), 1000),
]);
// 等候 2 秒 …
const list = async () => {
try {
const statuses = await statusesPromise;
console.log(statuses);
} catch (error) {
console.log(error);
}
};
list(); // // [{ status: rejected, reason: Error(Fruits is empty) },{ status: rejected, reason: Error(Vegetables is empty) }]
小结
当须要执行并行和独立的促发器操作形式并收集所有结论时,Promise.allSettled() 是不错的选择,即使一些促发器操作形式可能失败。
Promise.race()
方式 Promise.race(promises)promise 。
下面来看看 Promise.race() 是如何工作的。
1.工作原理
Promise.race() 回到两个 promise,一旦迭代器中的某个 promise 履行或拒绝,回到的 promise 就会履行或拒绝。
表达式接受两个 promise 数组(或通常是两个可迭代的)做为参数,如下:
const racePromise = Promise.race(promises);
当所有输入 promises 中有两个 promise 快速被履行或拒绝时,racePromise 就会解析快速完成的 promise 结论(履行或拒绝):
能选用 then 方式提取 racePromise 的结论:
racePromise.then((fastValue) => {
fastValue // 快速完成的 promise
});
也能选用 async/await 语法:
const fastPromise = await racePromise;
fastPromise; // 快速完成的 promise
Promise.race() 回到的承诺和最先完成的承诺信息一致。
Promise.race() 和 Promise.any() 的不同:Promise.race() 承诺列表中寻找第两个履行或拒绝的承诺;Promise.any() 是从承诺列表中查找第两个履行的承诺。2. 选用指南
那时来深入如是说 Promise.race() 的选用之前,同样先来定义 2 个简单的表达式。
function resolveTimeout(value, delay) {
return new Promise((resolve) => setTimeout(() => resolve(value), delay));
}
function rejectTimeout(reason, delay) {
return new Promise((r, reject) => setTimeout(() => reject(reason), delay));
}
接下来选用上面定义的2个辅助表达式来试试 Promise.race() 。
2.1 完成所有 promises
下面定义了两个 promise 数组 racePromise ,所有的 promise 都能够成功的 resolve 值,如下:
const fruits = [“potatoes”, “tomatoes”];
const vegetables = [“oranges”, “apples”];
const racePromise = Promise.race([
resolveTimeout(fruits, 5000),
resolveTimeout(vegetables, 1000),
]);
// 等候 1 秒 …
const list = async () => {
try {
const fastPromise = await racePromise;
console.log(fastPromise);
} catch (error) {
console.log(error);
}
};
list(); // [ oranges, apples ]
从上面执行的结论来看 Promise.race() 回到最快履行的 promise 的 resolve 结论。
2.2 两个 promise 被 rejected
将上面第两个 promise 再次出现极度被 rejected ,如下代码:
const fruits = [“potatoes”, “tomatoes”];
const racePromise = Promise.race([
resolveTimeout(fruits, 2000),
rejectTimeout(new Error(“Vegetables is empty”), 1000),
]);
// 等候 1 秒 …
const list = async () => {
try {
const fastPromise = await racePromise;
console.log(fastPromise);
} catch (error) {
console.log(error);
}
};
list(); // Error: Vegetables is empty
从上面的结论看,最先完成的 promise 被 rejected,那么 fastPromise 回到的 promise 也是被 rejected 。
下面将rejected 的承诺时间延长到 5秒,如下:
const fruits = [“potatoes”, “tomatoes”];
const racePromise = Promise.race([
resolveTimeout(fruits, 2000),
rejectTimeout(new Error(“Vegetables is empty”), 5000),
]);
// 等候 2 秒 …
const list = async () => {
try {
const fastPromise = await racePromise;
console.log(fastPromise);
} catch (error) {
console.log(error);
}
};
list(); // [ potatoes, tomatoes ]
从上面执行结论看到,最快完成的 promise 履行了 resolve ,那么 fastPromise 回到的 promise 也是履行了 resolve 。
2.3 所有 promises 被 rejected
将上面所有的 promises 再次出现极度被 rejected ,如下代码:
const racePromise = Promise.race([
rejectTimeout(new Error(“Fruits is empty”), 2000),
rejectTimeout(new Error(“Vegetables is empty”), 1000),
]);
// 等候 1 秒 …
const list = async () => {
try {
const fastPromise = await racePromise;
console.log(fastPromise);
} catch (error) {
console.log(error);
}
};
list(); // Error: Vegetables is empty
从结论来看,虽然两个承诺都被拒绝了,fastPromise 回到的 promise 是最快被拒绝的 。
3. 选用场景
在有促发器操作形式的工程项目中,在对于网络或统计数据库允诺进行性能进行优化的此时候,能选用 Promises 来测试其优化效果,透过选用 Promise.race() 来测试两种不同的方式的积极响应速度。
3.2 最差选择
发送允诺,只要其中两个完成工作,就将其统计数据呈现,达到选择最差线路的效果。这是能选用 Promise.race() 同时执行 promise 并在第两个成功后立即完成。
小结
Promise.race() 为第两个已解决和已拒绝的 promise 执行反弹表达式,而 Promise.any() 为第两个已履行的 promise 执行反弹表达式,如果没有履行的 promise 则拒绝两个特殊属性 AggregateError 。