Promise静态类型有哪些,分别有什么不同呢
Admin 2022-09-06 群英技术资讯 314 次浏览
恰逢 Promise
也有四个很像的静态三兄弟(Promise.all
、Promise.allSettled
、Promise.race
、Promise.any
),它们接受的参数类型相同,但各自逻辑处理不同,它们具体会有什么区别那?别急,下面等小包慢慢道来。
在文章的开始,小包先给大家提出几个问题:
Promise.all
与 Promise.allSettled
有啥区别啊?Promise.race
的运行机制? Promise.any
呐,两者有啥区别?Promise.all
在目前手写题中热度频度应该是 top5
级别的,所以我们要深刻掌握 Promise.all
方法。下面首先来简单回顾一下 all
方法。
Promise.all
方法类似于一群兄弟们并肩前行,参数可以类比为一群兄弟,只有当兄弟全部快乐,all
老大才会收获快乐;只要有一个兄弟不快乐,老大就不会快乐。
Promise.all()
方法用于将多个 Promise
实例,包装成一个新的 Promise
实例。
const p = Promise.all([p1, p2, p3]);
Promise.all
方法接受一个数组做参数,p1、p2、p3
都是 Promise
实例。如果不是 Promise
实例,则会先调用 Promise.resolve
方法将参数先转化为 Promise
实例,之后进行下一步处理。
返回值 p 的状态由 p1、p2、p3 决定,可以分成两种情况:
只有 p1、p2、p3
的状态都变成 fulfilled
,p
的状态才会变成 fulfilled
,此时 p1、p2、p3
的返回值组成一个数组,传递给 p
的回调函数。
只要 p1、p2、p3
之中有一个被 rejected
,p
的状态就变成 rejected
,此时第一个被 reject
的实例的返回值,会传递给 p
的回调函数。
// 模拟异步的promise
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
// 普通promise
const p2 = Promise.resolve(2);
// 常数值
const p3 = 3;
// 失败的promise
const p4 = Promise.reject("error");
// 异步失败的promise
const p5 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("TypeError");
}, 1000);
});
// 1. promise全部成功
Promise.all([p1, p2, p3])
.then((data) => console.log(data)) // [1, 2, 3]
.catch((error) => console.log(error));
// 2. 存在失败的promise
Promise.all([p1, p2, p3, p4])
.then((data) => console.log(data))
.catch((error) => console.log(error)); // error
// 3. 存在多个失败的promise
Promise.all([p1, p2, p3, p4, p5])
.then((data) => console.log(data))
.catch((error) => console.log(error)); // error
从上面案例的输出中,我们可以得出下列结论:
p
状态由参数执行结果决定,全部成功则返回成功,存有一个失败则失败Promise
实例,会通过 Promise.resolve
转化成 Promise
实例《ES6 入门教程》还指出: Promise.all 方法可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例
说实话,加粗部分小包是没能完全理解的,难道 Promise.all
使用 Iterator
类型时,要求迭代项都是 Promise
实例吗?我们以 String
类型为例,看 Promise.all
是否可以支持迭代项为非 Promise
实例。
// ['x', 'i', 'a', 'o', 'b', 'a', 'o']
Promise.all("xiaobao").then((data) => console.log(data));
可见 Promise
对 Iterator
类型的处理与数组相同,如果参数不是 Promise
实例,会先调用 Promise.all
转化为 Promise
实例。
Promise.all
会返回一个新 Promise
对象Promise.all = function (promises) {
return new Promise((resolve, reject) => {});
};
all
方法参数可以是数组,同样也可以是 Iterator
类型,因此应该使用 for of
循环进行遍历。Promise.all = function (promises) {
return new Promise((resolve, reject) => {
for (let p of promises) {
}
});
};
Promise
类型,因此参数使用前先通过 Promise.resolve
转换Promise.all = function (promises) {
return new Promise((resolve, reject) => {
for (let p of promises) {
// 保证所有的参数为 promise 实例,然后执行后续操作
Promise.resolve(p).then((data) => {
//...
});
}
});
};
Iterator
类型我们是无法得知迭代深度,因此我们要维护一个 count
用来记录 promise
总数,同时维护 fulfilledCount
代表完成的 promise
数,当 count === fulfilledCount
,代表所有传入的 Promise
执行成功,返回数据。Promise.all = function (promises) {
let count = 0; // promise总数
let fulfilledCount = 0; // 完成的promise数
return new Promise((resolve, reject) => {
for (let p of promises) {
count++; // promise总数 + 1
Promise.resolve(p).then((data) => {
fulfilledCount++; // 完成的promise数量+1
if (count === fulfilledCount) {
// 代表最后一个promise完成了
resolve();
}
});
}
});
};
有可能有的读者会好奇,为啥 count === fulfilledCount
可以判断所有的 promise 都完成了呐?
Promise.then
方法是 microTasks
(微任务),当同步任务执行完毕后,Event Loop
才会去执行 microTasks
。count++
位于同步代码部分,因此在执行 promise.then
方法之前,已经成功的计算出 promise
的总数。
然后依次执行 promise.then
方法,fulfilledCount
增加,当 count === fulfilledCount
说明所有的 promise
都已经成功完成了。
all
方法中比较难处理的部分。result
存储所有 promise
成功的数据for of
循环中,使用 let
变量定义 i
,其值等于当前的遍历索引let
定义的变量不会发生变量提升,因此我们直接令 result[i]
为 promise
成功数据,这样就可以实现按参数输入顺序输出结果Promise.all = function (promises) {
const result = []; // 存储promise成功数据
let count = 0;
let fulfilledCount = 0;
return new Promise((resolve, reject) => {
for (let p of promises) {
// i为遍历的第几个promise
// 使用let避免形成闭包问题
let i = count;
count++;
// 保证所有的参数为 promise 实例,然后执行后续操作
Promise.resolve(p).then((data) => {
fulfilledCount++;
// 将第i个promise成功数据赋值给对应位置
result[i] = data;
if (count === fulfilledCount) {
// 代表最后一个promise完成了
// 返回result数组
resolve(result);
}
});
}
});
};
promise
失败——直接调用 reject
即可promise
数量为 0
——返回空数组(规范规定)// 多余代码省略
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
// 3.捕获代码执行中的异常
try{
for (let p of promises) {
Promise.resolve(p).then(data => {}
.catch(reject); // 1.直接调用reject函数返回失败原因
})
}
// 2.传入promise数量为0
if (count === 0) {
resolve(result)
}
} catch(error) {
reject(error)
}
})
}
我们把上面的代码汇总一下,加上详细的注释,同时测试一下手写 Promise.all
是否成功。
Promise.all = function (promises) {
const result = []; // 存储promise成功数据
let count = 0; // promise总数
let fulfilledCount = 0; //完成promise数量
return new Promise((resolve, reject) => {
// 捕获代码执行中的异常
try {
for (let p of promises) {
// i为遍历的第几个promise
// 使用let避免形成闭包问题
let i = count;
count++; // promise总数 + 1
Promise.resolve(p)
.then((data) => {
fulfilledCount++; // 完成的promise数量+1
// 将第i个promise成功数据赋值给对应位置
result[i] = data;
if (count === fulfilledCount) {
// 代表最后一个promise完成了
// 返回result数组
resolve(result);
}
})
.catch(reject);
// 传入promise数量为0
if (count === 0) {
resolve(result); // 返回空数组
}
}
} catch (error) {
reject(error);
}
});
};
测试代码(使用案例中的测试代码,附加 Iterator
类型 Stirng
):
// 1. promise全部成功
Promise.all([p1, p2, p3])
.then((data) => console.log(data)) // [1, 2, 3]
.catch((error) => console.log(error));
// 2. 存在失败的promise
Promise.all([p1, p2, p3, p4])
.then((data) => console.log(data))
.catch((error) => console.log(error)); // error
// 3. 存在多个失败的promise
Promise.all([p1, p2, p3, p4, p5])
.then((data) => console.log(data))
.catch((error) => console.log(error)); // error
// 4. String 类型
Promise.all("zcxiaobao").then((data) => console.log(data));
// ['z', 'c', 'x', 'i', 'a', 'o', 'b', 'a', 'o']
不是每群兄弟们都会碰到好老大(
all
方法),allSettled
方法他并不管兄弟们的死活,他只管兄弟们是否做了,而他的任务就是把所有兄弟的结果返回。
Promise.allSettled()
方法接受一个数组作为参数,数组的每个成员都是一个 Promise
对象,并返回一个新的 Promise
对象。只有等到参数数组的所有 Promise
对象都发生状态变更(不管是 fulfilled
还是 rejected
),返回的 Promise
对象才会发生状态变更。
还是以上面的例子为例,我们来看一下与 Promise.all
方法有啥不同。
// 1. promise 全部成功
Promise.allSettled([p1, p2, p3])
.then((data) => console.log(data)) // [1, 2, 3]
.catch((error) => console.log(error));
// 2. 存在失败的 promise
Promise.allSettled([p1, p2, p3, p4])
.then((data) => console.log(data))
.catch((error) => console.log(error)); // error
// 3. 存在多个失败的 promise
Promise.allSettled([p1, p2, p3, p4, p5])
.then((data) => console.log(data))
.catch((error) => console.log(error)); // error
// 4. 传入 String 类型
Promise.allSettled("zc").then((data) => console.log(data));
从输出结果我们可以发现:
allSettled
方法只会成功,不会失败promise
成功,对象属性值 status: fulfilled
,value
记录成功值status: rejected
,reason
记录失败原因。allSettled
方法也可以接受 Iterator
类型参数allSettled
方法与 all
方法最大的区别在于两点:
allSettled
方法没有失败情况allSettled
方法返回有固定格式我们可以围绕这两点改造 all
方法。
all
方法我们是通过计算成功数量来判断是否终结,allSettled
方法不计较成功失败,因此我们需要计算成功/失败总数量即可。
在累加完成总数量的过程中,分情况构造 allSettled
所需要的数据格式: 成功时压入成功格式,失败时压入失败格式。
由于有了 all
方法手写的基础,上面就不一步一步啰嗦的实现了。
Promise.allSettled = function (promises) {
const result = [];
let count = 0;
let totalCount = 0; //完成promise数量
return new Promise((resolve, reject) => {
try {
for (let p of promises) {
let i = count;
count++; // promise总数 + 1
Promise.resolve(p)
.then((res) => {
totalCount++;
// 成功时返回成功格式数据
result[i] = {
status: "fulfilled",
value: res,
};
// 执行完成
if (count === totalCount) {
resolve(result);
}
})
.catch((error) => {
totalCount++;
// 失败时返回失败格式数据
result[i] = {
status: "rejected",
reason: error,
};
// 执行完成
if (count === totalCount) {
resolve(result);
}
});
if (count === 0) {
resolve(result);
}
}
} catch (error) {
reject(error);
}
});
};
race
方法形象化来讲就是赛跑机制,只认第一名,不管是成功的第一还是失败的第一。
Promise.race()
方法同样是接收多个 Promise
实例,包装成一个新的 Promise
实例。
const p = Promise.race([p1, p2, p3]);
上面案例中,只要 p1、p2、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise
实例的返回值,就传递给 p
的回调函数。
const p1 = new Promise((resolve, reject) => {
setTimeout(()=> {
resolve(1)
},1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(()=> {
reject(2)
},2000)
})
const p3 = 3;
// 成功在先,失败在后
Promise.race([p1, p2]).then(res => {console.log(res)}) // 1
// 同步在先,异步在后
Promise.race([p1, p3]).then(res => console.log(res)) // 3
// String
Promise.race('zc').then(res => console.log(res)) // z
race
方法就没有那么多弯弯绕绕了,只要某个 promise
改变状态就返回其对应结果。
因此我们只需监听每个 promise
的 then
与 catch
方法,当发生状态改变,直接调用 resolve
和 reject
方法即可。
Promise.race(promises) {
return new Promise((resolve, reject) => {
for (let p of promises) {
// Promise.resolve将p进行转化,防止传入非Promise实例
// race执行机制为那个实例发生状态改变,则返回其对应结果
// 因此监听
Promise.resolve(p).then(resolve).catch(reject);
}
})
}
any 方法形象化来说是天选唯一,只要第一个成功者。如果全部失败了,就返回失败情况。
ES2021
引入了 Promise.any()
方法。该方法接受一组 Promise
实例作为参数,包装成一个新的 Promise
实例返回。
any
方法与 race
方法很像,也存在短路特性,只要有一个实例变成 fulfilled
状态,就会返回成功的结果;如果全部失败,则返回失败情况。
// 成功的promise
const p1 = new Promise((resolve, reject) => {
setTimeout(()=> {
resolve(1)
},1000)
})
// 失败的promise
const p2 = new Promise((resolve, reject) => {
setTimeout(()=> {
reject(2)
},2000)
})
//失败的promise
const p3 = new Promise((resolve, reject) => {
reject(3)
})
// 存在一个成功的promise
Promise.any([p1,p2]).then(res => console.log(res))// 1
// 全部失败的promise
Promise.any([p2,p3]).then(res => console.log(res))
.catch(error => console.log(error)) // AggregateError: All promises were rejected
// String类型
Promise.any('zc').then(res => console.log(res)) // z
通过上述输出结果我们可以发现:
any
方法也可以接受 Iterator
格式参数promise
实例转变为 fulfilled
时,any
返回成功的 promise
,值为最早成功的 promise
值。promise
全部失败时,any
返回失败的 promise
,值固定为 AggregateError: All promises were rejected
上面我们分析了 any
方法的机制:
fulfilled
,any
随之返回成功的 promise
。因此这里我们就可以类似使用 race
的方法,监测每个 promise
的成功。rejected
,any
返回 AggregateError: All promises were rejected
。这里我们可以参考 all
方法的全部成功,才返回成功,因此我们需要累计失败数量,当 rejectCount === count
时,返回失败值。Promise.any = function(promises) {
return new Promise((resolve,reject) => {
let count = 0;
let rejectCount = 0;
let errors = [];
let i = 0;
for (let p of promises) {
i = count;
count ++;
Promise.resolve(p).then(res => {
resolve(res)
}).catch(error => {
errors[i] = error;
rejectCount ++;
if (rejectCount === count) {
return reject(new AggregateError(errors))
}
})
}
if(count === 0) return reject(new AggregateError('All promises were rejected'))
})
}
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
这篇文章主要介绍了 JavaScript 与 TypeScript之间的联系,JavaScript,也称为 JS,是一种符合 ECMAScript 规范的编程语言。这是一个高级别的、通常是即时编译的、多范式的。TypeScript 是一种强类型、面向对象的编译语言,更多消息内容,需要的朋友可以参考一下下面文章内容
JS沙箱的实现方法有哪些?在微前端领域当中,沙箱是很重要的一件事情,下文会给大家介绍iframe实现沙箱、diff方式实现沙箱、基于代理(Proxy)实现单实例沙箱和基于代理(Proxy)实现多实例沙箱这四种JS沙箱的实现方法,感兴趣的朋友就继续往下看吧。
这篇文章主要为大家详细介绍了微信小程序实现简单计算器功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
在React中,你可以创建不同的组件来封装各种你需要的行为。然后还可以根据应用的状态变化只渲染其中的一部分。React 中的条件渲染和JavaScript中的一致,使用JavaScript操作符if或条件运算符来创建表示当前状态的元素,然后让React根据它们来更新UI
1.根据端口号,查询进程信息命令:netstat-aon|findstr"端口号"2.根据pid杀死进程命令:taskkill/F/pid进程号 完整代码:constwindowsServiceStop=function(port){letnetstat_command='netstat-aon|findstr\":'+po
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008