以案例引出Promise,深入浅出,合理
链式调用直接跳至第2章
静态方法跳转第3章
async、await跳转第4章
第5章为前四章的练习
1- Promise基础
张三的烦恼
张三心中有很多女神,他今天下定决心,要向这些女神表白,他认为,只要女神够多,根据概率学原理,总有一个会接收他
稳妥起见,张三决定使用串行的方式进行表白:先给第1位女神发送短信,然后等待女神的回应,如果成功了,就结束,如果失败了,则再给第2位女神发送短信,依次类推
张三的女神一共有4位,名字分别是:女神1、王富贵、周聚财、刘人勇
发短信是一个重复性的劳动,张三是个程序员,因此决定用函数封装这个动作
javascript
// 向某位女生发送一则表白短信
// name: 女神的姓名
// onFulffiled: 成功后的回调
// onRejected: 失败后的回调
function sendMessage(name, onFulffiled, onRejected) {
// 模拟 发送表白短信
console.log(
`张三 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘`
);
console.log(`等待${name}回复......`);
// 模拟 女神回复需要一段时间
setTimeout(() => {
// 模拟 有10%的几率成功
if (Math.random() <= 0.1) {
// 成功,调用 onFuffiled,并传递女神的回复
onFulffiled(`${name} -> 张三:我是九,你是三,除了你还是你😘`);
} else {
// 失败,调用 onRejected,并传递女神的回复
onRejected(`${name} -> 张三:你是个好人😜`);
}
}, 1000);
}
有了这个函数后,张三于是开始编写程序发送短信了
javascript
// 首先向 女神1 发送消息
sendMessage(
'女神1',
(reply) => {
// 如果成功了,输出回复的消息后,结束
console.log(reply);
},
(reply) => {
// 如果失败了,输出回复的消息后,向 王富贵 发送消息
console.log(reply);
sendMessage(
'王富贵',
(reply) => {
// 如果成功了,输出回复的消息后,结束
console.log(reply);
},
(reply) => {
// 如果失败了,输出回复的消息后,向 周聚财 发送消息
console.log(reply);
sendMessage(
'周聚财',
(reply) => {
// 如果成功了,输出回复的消息后,结束
console.log(reply);
},
(reply) => {
// 如果失败了,输出回复的消息后,向 刘人勇 发送消息
console.log(reply);
sendMessage(
'刘人勇',
(reply) => {
// 如果成功了,输出回复的消息后,结束
console.log(reply);
},
(reply) => {
// 如果失败了,就彻底没戏了
console.log(reply);
console.log('张三命犯天煞孤星,注定孤独终老!!');
}
);
}
);
}
);
}
);
该程序完成后,张三内心是崩溃的
这一层一层的回调嵌套,形成了传说中的「回调地狱 callback hell」
张三是个完美主义者,怎么能忍受这样的代码呢?
要解决这样的问题,需要Promise出马
Promise规范
Promise是一套专门处理异步场景的规范,它能有效的避免回调地狱的产生,使异步代码更加清晰、简洁、统一
这套规范最早诞生于前端社区,规范名称为Promise A+
该规范出现后,立即得到了很多开发者的响应
Promise A+ 规定:
-
所有的异步场景,都可以看作是一个异步任务,每个异步任务,在JS中应该表现为一个对象 ,该对象称之为Promise对象,也叫做任务对象
-
每个任务对象,都应该有两个阶段、三个状态
根据常理,它们之间存在以下逻辑:
- 任务总是从未决阶段变到已决阶段,无法逆行
- 任务总是从挂起状态变到完成或失败状态,无法逆行
- 时间不能倒流,历史不可改写,任务一旦完成或失败,状态就固定下来,永远无法改变
-
挂起->完成
,称之为resolve
;挂起->失败
称之为reject
。任务完成时,可能有一个相关数据;任务失败时,可能有一个失败原因。 -
可以针对任务进行后续处理,针对完成状态的后续处理称之为onFulfilled ,针对失败的后续处理称之为onRejected
Promise API
ES6提供了一套API,实现了Promise A+规范
基本使用如下:
javascript
// 创建一个任务对象,该任务立即进入 pending 状态
const pro = new Promise((resolve, reject) => {
// 任务的具体执行流程,该函数会立即被执行
// 调用 resolve(data),可将任务变为 fulfilled 状态, data 为需要传递的相关数据
// 调用 reject(reason),可将任务变为 rejected 状态,reason 为需要传递的失败原因
});
pro.then(
(data) => {
// onFulfilled 函数,当任务完成后,会自动运行该函数,data为任务完成的相关数据
},
(reason) => {
// onRejected 函数,当任务失败后,会自动运行该函数,reason为任务失败的相关原因
}
);
张三的解决方案
学习了ES6的Promise后,张三决定对sendMessage
函数进行改造,改造结果如下:
javascript
// 向某位女生发送一则表白短信
// name: 女神的姓名
// 该函数返回一个任务对象
function sendMessage(name) {
return new Promise((resolve, reject) => {
// 模拟 发送表白短信
console.log(
`张三 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘`
);
console.log(`等待${name}回复......`);
// 模拟 女神回复需要一段时间
setTimeout(() => {
// 模拟 有10%的几率成功
if (Math.random() <= 0.1) {
// 成功,调用 resolve,并传递女神的回复
resolve(`${name} -> 张三:我是九,你是三,除了你还是你😘`);
} else {
// 失败,调用 reject,并传递女神的回复
reject(`${name} -> 张三:你是个好人😜`);
}
}, 1000);
});
}
之后,就可以使用该函数来发送消息了
javascript
sendMessage('女神1').then(
(reply) => {
// 女神答应了,输出女神的回复
console.log(reply);
},
(reason) => {
// 女神拒绝了,输出女神的回复
console.log(reason);
}
);
至此,回调地狱的问题仍然没能解决
要解决回调地狱,还需要进一步学习Promise的知识
2- Promise的链式调用
catch方法
.catch(onRejected)
= .then(null, onRejected)
链式调用
-
then方法必定会返回一个新的Promise
可理解为
后续处理也是一个任务
-
新任务的状态取决于后续处理:
-
若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据
-
若有后续处理但还未执行,新任务挂起。
-
若后续处理执行了,则根据后续处理的情况确定新任务的状态
- 后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值
- 后续处理执行有错,新任务的状态为失败,数据为异常对象
- 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一致
-
由于链式任务的存在,异步代码拥有了更强的表达力
scss
// 常见任务处理代码
/*
* 任务成功后,执行处理1,失败则执行处理2
*/
pro.then(处理1).catch(处理2)
/*
* 任务成功后,依次执行处理1、处理2
*/
pro.then(处理1).then(处理2)
/*
* 任务成功后,依次执行处理1、处理2,若任务失败或前面的处理有错,执行处理3
*/
pro.then(处理1).then(处理2).catch(处理3)
张三的解决方案
javascript
// 向某位女生发送一则表白短信
// name: 女神的姓名
// onFulffiled: 成功后的回调
// onRejected: 失败后的回调
function sendMessage(name) {
return new Promise((resolve, reject) => {
// 模拟 发送表白短信
console.log(
`张三 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘`
);
console.log(`等待${name}回复......`);
// 模拟 女神回复需要一段时间
setTimeout(() => {
// 模拟 有10%的几率成功
if (Math.random() <= 0.1) {
// 成功,调用 onFuffiled,并传递女神的回复
resolve(`${name} -> 张三:我是九,你是三,除了你还是你😘`);
} else {
// 失败,调用 onRejected,并传递女神的回复
reject(`${name} -> 张三:你是个好人😜`);
}
}, 1000);
});
}
sendMessage('女神1')
.catch((reply) => {
// 失败,继续
console.log(reply);
return sendMessage('王富贵');
})
.catch((reply) => {
// 失败,继续
console.log(reply);
return sendMessage('周聚财');
})
.catch((reply) => {
// 失败,继续
console.log(reply);
return sendMessage('刘人勇');
})
.then(
(reply) => {
// 成功,结束
console.log(reply);
console.log('张三终于找到了自己的伴侣');
},
(reply) => {
// 最后一个也失败了
console.log(reply);
console.log('张三命犯天煞孤星,无伴终老,孤独一生');
}
);
3- Promise的静态方法
张三的新问题
张嫂出门时,给张三交待了几个任务:
-
做饭
可交给电饭煲完成
-
洗衣服
可交给洗衣机完成
-
打扫卫生
可交给扫地机器人完成
张三需要在所有任务结束后给张嫂汇报工作,哪些成功了,哪些失败了
为了最大程度的节约时间,张三希望这些任务同时进行,最终汇总结果统一处理
每个任务可以看做是一个返回Promise的函数
javascript
// 做饭
function cook() {
return new Promise((resolve, reject) => {
console.log('张三打开了电饭煲');
setTimeout(() => {
if (Math.random() < 0.5) {
resolve('饭已ok');
} else {
reject('做饭却忘了加水,米饭变成了爆米花');
}
}, 2000);
});
}
// 洗衣服
function wash() {
return new Promise((resolve, reject) => {
console.log('张三打开了洗衣机');
setTimeout(() => {
if (Math.random() < 0.5) {
resolve('衣服已经洗好');
} else {
reject('洗衣服时停水了,洗了个寂寞');
}
}, 2500);
});
}
// 打扫卫生
function sweep() {
return new Promise((resolve, reject) => {
console.log('张三打开了扫地机器人');
setTimeout(() => {
if (Math.random() < 0.5) {
resolve('地板扫的非常干净');
} else {
reject('扫地机器人被哈士奇一爪掀翻了');
}
}, 3000);
});
}
如何利用这三个函数实现张三的要求呢?
Promise的静态方法
方法名 | 含义 |
---|---|
Promise.resolve(data) | 直接返回一个完成状态的任务 |
Promise.reject(reason) | 直接返回一个拒绝状态的任务 |
Promise.all(任务数组) | 返回一个任务 任务数组全部成功则成功 任何一个失败则失败 |
Promise.any(任务数组) | 返回一个任务 任务数组任一成功则成功 任务全部失败则失败 |
Promise.allSettled(任务数组) | 返回一个任务 任务数组全部已决则成功 该任务不会失败 |
Promise.race(任务数组) | 返回一个任务 任务数组任一已决则已决,状态和其一致 |
张三的解决方案
scss
Promise.allSettled([cook(), wash(), sweep()]).then((result) => {
// 处理汇总结果
const report = result
.map((r) => (r.status === 'fulfilled' ? r.value : r.reason))
.join(';');
console.log(report);
});
4- async和await
消除回调
有了Promise,异步任务就有了一种统一的处理方式
有了统一的处理方式,ES官方就可以对其进一步优化
ES7推出了两个关键字async
和await
,用于更加优雅的表达Promise
async
async关键字用于修饰函数,被它修饰的函数,一定返回Promise
javascript
async function method1(){
return 1; // 该函数的返回值是Promise完成后的数据
}
method1(); // Promise { 1 }
async function method2(){
return Promise.resolve(1); // 若返回的是Promise,则method得到的Promise状态和其一致
}
method2(); // Promise { 1 }
async function method3(){
throw new Error(1); // 若执行过程报错,则任务是rejected
}
method3(); // Promise { <rejected> Error(1) }
await
await
关键字表示等待某个Promise完成,它必须用于async
函数中
javascript
async function method(){
const n = await Promise.resolve(1);
console.log(n); // 1
}
// 上面的函数等同于
function method(){
return new Promise((resolve, reject)=>{
Promise.resolve(1).then(n=>{
console.log(n);
resolve(1)
})
})
}
await
也可以等待其他数据
csharp
async function method(){
const n = await 1; // 等同于 await Promise.resolve(1)
}
如果需要针对失败的任务进行处理,可以使用try-catch
语法
javascript
async function method(){
try{
const n = await Promise.reject(123); // 这句代码将抛出异常
console.log('成功', n)
}
catch(err){
console.log('失败', err)
}
}
method(); // 输出: 失败 123
张三表白的完美解决方案
张三的女神可不是只有4位,而是40位!
为了更加方便的编写表白代码,张三决定把这40位女神放到一个数组中,然后利用async和await轻松完成代码
javascript
// 女神的名字数组
const beautyGirls = [
'梁平',
'邱杰',
'王超',
......
];
// 向某位女生发送一则表白短信
// name: 女神的姓名
function sendMessage(name) {
return new Promise((resolve, reject) => {
// 模拟 发送表白短信
console.log(
`张三 -> ${name}:最近有谣言说我喜欢你,我要澄清一下,那不是谣言😘`
);
console.log(`等待${name}回复......`);
// 模拟 女神回复需要一段时间
setTimeout(() => {
// 模拟 有10%的几率成功
if (Math.random() <= 0.1) {
// 成功,调用 onFuffiled,并传递女神的回复
resolve(`${name} -> 张三:我是九,你是三,除了你还是你😘`);
} else {
// 失败,调用 onRejected,并传递女神的回复
reject(`${name} -> 张三:你是个好人😜`);
}
}, 1000);
});
}
// 批量表白的程序
async function proposal() {
let isSuccess = false;
for (const girl of beautyGirls) {
try {
const reply = await sendMessage(girl);
console.log(reply);
console.log('表白成功!');
isSuccess = true;
break;
} catch (reply) {
console.log(reply);
console.log('表白失败');
}
}
if (!isSuccess) {
console.log('张三注定孤独一生');
}
}
proposal();
练习题
1-Promise基础练习
1-完成delay函数的补充
javascript
//1. 完成下面的函数
/**
* 延迟一段指定的时间
* @param {Number} duration 等待的时间
* @returns {Promise} 返回一个任务,该任务在指定的时间后完成
*/
function delay(duration) {
//补充内容
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, duration);
});
}
// 2. 按照要求,调用delay函数,完成程序
// 利用delay函数,等待1秒钟,输出:finish
delay(1000).then(() => {
console.log('finish');
});
2- 完成图片操作
javascript
// 根据指定的图片路径,创建一个img元素
// 该函数需要返回一个Promise,当图片加载完成后,任务完成,若图片加载失败,任务失败
// 任务完成后,需要提供的数据是图片DOM元素;任务失败时,需要提供失败的原因
// 提示:img元素有两个事件,load事件会在图像加载完成时触发,error事件会在图像加载失败时触发
function createImage(imgUrl) {}
// 使用createImage函数创建一个图像,图像路径自行定义
// 当图像成功加载后,将图像宽高显示在p元素中,当图像加载失败后,输出加载失败的原因
// 使用createImage函数创建一个图像,图像路径自行定义
// 当图像成功加载后,将图像元素加入到container容器中,当图像加载失败后,输出加载失败的原因
javascript
function createImage(imgUrl) {
return new Promise((resolve, reject) => {
const img = document.createElement('img');
img.src = imgUrl;
img.onload = () => {
// 图像加载完成
resolve(img);
};
img.onerror = (e) => {
// 图像加载失败
reject(e);
};
});
}
// 使用createImage函数创建一个图像,图像路径自行定义
// 当图像成功加载后,将图像宽高显示在p元素中,当图像加载失败后,输出加载失败的原因
const url1 =
'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/151a34f082bf4d78b1c51a9642db29d1~tplv-k3u1fbpfcp-jj:240:200:0:0:q75.avis#?w=2080&h=960&s=929926&e=jpg&b=d4f3ec';
createImage(url1).then(
(img) => {
const p = document.querySelector('.label');
p.innerHTML = `${img.width} * ${img.height}`;
},
(reason) => {
console.log(reason);
}
);
// 使用createImage函数创建一个图像,图像路径自行定义
// 当图像成功加载后,将图像元素加入到container容器中,当图像加载失败后,输出加载失败的原因
createImage(url1).then(
(img) => {
const div = document.querySelector('.container');
div.appendChild(img);
},
(reason) => {
console.log(reason);
}
);
3- 完成省份信息渲染
javascript
// 调用该函数,会远程加载省份数据
// 函数返回一个Promise,成功后得到省份数组,失败时会给予失败原因
function getProvinces() {
return fetch('/api/citylist')
.then((resp) => resp.json())
.then((resp) => resp.data)
.then((resp) =>
resp.map((it) => ({ value: it.value, label: it.label }))
);
}
// 利用getProvinces函数,将省份数据加载到select元素中
getProvinces().then(
(ps) => {
const html = ps
.map((p) => `<option value="${p.value}">${p.label}</option>`)
.join('');
const selProvince = document.getElementById('selProvince');
selProvince.innerHTML = html;
},
(reason) => {
console.log(reason);
}
);
3-Promise链式调用练习
1-获取学生数据
javascript
/**
* 根据页码获取学生数据,返回Promise
* @param {Number} page 页码
*/
function fetchStudents(page) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() < 0.3) {
reject(new Error(`网络错误!获取第${page}页数据失败!`));
return;
}
// 模拟学生数据
const stus = new Array(10).fill(null).map((d, i) => ({
id: `NO.${(page - 1) * 10 + i + 1}`,
name: `姓名${(page - 1) * 10 + i + 1}`,
}));
resolve(stus);
}, Math.floor(Math.random() * 5000));
});
}
// 利用 fetchStudents 函数,完成下面的练习
// 获取1-10页的学生,最终按照页码的顺序合并成一个数组,任何一页的数据获取出现错误,则任务不再继续,打印错误消息
// 获取1-10页的学生,最终按照页码的顺序合并成一个数组,如果某些页码的数据获取失败,就不加入该数据即可
// 获取1-10页的学生,打印最先获取到的数据,如果全部都获取失败,则打印所有的错误消息
// 获取1-10页的学生,输出最先得到的结果(有结果输出结果,有错误输出错误)
javascript
// 获取1-10页的学生,最终按照页码的顺序合并成一个数组,任何一页的数据获取出现错误,则任务不再继续,打印错误消息
const proms = new Array(10).fill(1).map((it, i) => fetchStudents(i + 1));
Promise.all(proms)
.then((result) => {
console.log(result.flat());
})
.catch((err) => {
console.log(err);
});
// 获取1-10页的学生,最终按照页码的顺序合并成一个数组,如果某些页码的数据获取失败,就不加入该数据即可
Promise.allSettled(proms).then((result) => {
result = result
.filter((r) => r.status === 'fulfilled')
.map((it) => it.value)
.flat();
console.log(result);
});
// 获取1-10页的学生,打印最先获取到的数据,如果全部都获取失败,则打印所有的错误消息
Promise.any(proms)
.then((result) => {
console.log(result);
})
.catch((err) => {
console.log(err.errors);
});
// 获取1-10页的学生,输出最先得到的结果(有结果输出结果,有错误输出错误)
Promise.race(proms).then(
(result) => {
console.log(result);
},
(err) => {
console.log(err);
}
);
4-aysnc和await练习
1-获取英雄数据渲染
javascript
async function getHeroes() {
return fetch('/api/herolist')
.then((resp) => resp.json())
.then((resp) => resp.data);
}
// 利用getHeroes方法,获取所有的英雄数据,将英雄名称显示到页面的列表中
ini
// 利用getHeroes方法,获取所有的英雄数据,将英雄名称显示到页面的列表中
const ul = document.getElementById('heroList');
(async () => {
const data = await getHeroes();
const result = data.map((d) => `<li>${d.cname}</li>`).join('');
ul.innerHTML = result;
})();
2-完成delay函数
javascript
// 完成delay函数
// 该函数可以等待一段指定的时间
// 返回Promise
function delay(duration) {}
// 利用delay函数,等待3次,每次等待1秒,每次等待完成后输出ok
// 等待1秒->ok->等待1秒->ok->等待1秒->ok
scss
// 利用delay函数,等待3次,每次等待1秒,每次等待完成后输出ok
// 等待1秒->ok->等待1秒->ok->等待1秒->ok
(async () => {
for (let i = 0; i < 3; i++) {
await delay(1000);
console.log('ok');
}
})();
delay(1000)
.then(() => {
console.log('ok');
return delay(1000);
})
.then(() => {
console.log('ok');
return delay(1000);
})
.then(() => {
console.log('ok');
});
手写Promise
kotlin
// 记录Promise的三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
/**
* 运行一个微队列任务
* 把传递的函数放到微队列中
* @param {Function} callback
*/
function runMicroTask(callback) {
// 判断node环境
// 为了避免「变量未定义」的错误,这里最好加上前缀globalThis
// globalThis是一个关键字,指代全局对象,浏览器环境为window,node环境为global
if (globalThis.process && globalThis.process.nextTick) {
process.nextTick(callback);
} else if (globalThis.MutationObserver) {
const p = document.createElement('p');
const observer = new MutationObserver(callback);
observer.observe(p, {
childList: true, // 观察该元素内部的变化
});
p.innerHTML = '1';
} else {
setTimeout(callback, 0);
}
}
/**
* 判断一个数据是否是Promise对象
* @param {any} obj
* @returns
*/
function isPromise(obj) {
return !!(obj && typeof obj === 'object' && typeof obj.then === 'function');
}
class MyPromise {
/**
* 创建一个Promise
* @param {Function} executor 任务执行器,立即执行
*/
constructor(executor) {
this._state = PENDING; // 状态
this._value = undefined; // 数据
this._handlers = []; // 处理函数形成的队列
try {
executor(this._resolve.bind(this), this._reject.bind(this));
} catch (error) {
this._reject(error);
console.error(error);
}
}
/**
* 向处理队列中添加一个函数
* @param {Function} executor 添加的函数
* @param {String} state 该函数什么状态下执行
* @param {Function} resolve 让then函数返回的Promise成功
* @param {Function} reject 让then函数返回的Promise失败
*/
_pushHandler(executor, state, resolve, reject) {
this._handlers.push({
executor,
state,
resolve,
reject,
});
}
/**
* 根据实际情况,执行队列
*/
_runHandlers() {
if (this._state === PENDING) {
// 目前任务仍在挂起
return;
}
while (this._handlers[0]) {
const handler = this._handlers[0];
this._runOneHandler(handler);
this._handlers.shift();
}
}
/**
* 处理一个handler
* @param {Object} handler
*/
_runOneHandler({ executor, state, resolve, reject }) {
runMicroTask(() => {
if (this._state !== state) {
// 状态不一致,不处理
return;
}
if (typeof executor !== 'function') {
// 传递后续处理并非一个函数
this._state === FULFILLED ? resolve(this._value) : reject(this._value);
return;
}
try {
const result = executor(this._value);
if (isPromise(result)) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (error) {
reject(error);
console.error(error);
}
});
}
/**
* Promise A+规范的then
* @param {Function} onFulfilled
* @param {Function} onRejected
*/
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this._pushHandler(onFulfilled, FULFILLED, resolve, reject);
this._pushHandler(onRejected, REJECTED, resolve, reject);
this._runHandlers(); // 执行队列
});
}
/**
* 仅处理失败的场景
* @param {Function} onRejected
*/
catch(onRejected) {
return this.then(null, onRejected);
}
/**
* 无论成功还是失败都会执行回调
* @param {Function} onSettled
*/
finally(onSettled) {
return this.then(
(data) => {
onSettled();
return data;
},
(reason) => {
onSettled();
throw reason;
}
);
}
/**
* 更改任务状态
* @param {String} newState 新状态
* @param {any} value 相关数据
*/
_changeState(newState, value) {
if (this._state !== PENDING) {
// 目前状态已经更改
return;
}
this._state = newState;
this._value = value;
this._runHandlers(); // 状态变化,执行队列
}
/**
* 标记当前任务完成
* @param {any} data 任务完成的相关数据
*/
_resolve(data) {
this._changeState(FULFILLED, data);
}
/**
* 标记当前任务失败
* @param {any} reason 任务失败的相关数据
*/
_reject(reason) {
this._changeState(REJECTED, reason);
}
/**
* 返回一个已完成的Promise
* 特殊情况:
* 1. 传递的data本身就是ES6的Promise对象
* 2. 传递的data是PromiseLike(Promise A+),返回新的Promise,状态和其保持一致即可
* @param {any} data
*/
static resolve(data) {
if (data instanceof MyPromise) {
return data;
}
return new MyPromise((resolve, reject) => {
if (isPromise(data)) {
data.then(resolve, reject);
} else {
resolve(data);
}
});
}
/**
* 得到一个被拒绝的Promise
* @param {any}} reason
*/
static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
/**
* 得到一个新的Promise
* 该Promise的状态取决于proms的执行
* proms是一个迭代器,包含多个Promise
* 全部Promise成功,则返回的Promise成功,数据为所有Promise成功的数据,并且顺序是按照传入的顺序排列
* 只要有一个Promise失败,则返回的Promise失败,原因是第一个失败的Promise的原因
* @param {iterator} proms
*/
static all(proms) {
return new MyPromise((resolve, reject) => {
try {
const results = [];
let count = 0; // Promise的总数
let fulfilledCount = 0; // 已完成的数量
for (const p of proms) {
let i = count;
count++;
MyPromise.resolve(p).then((data) => {
fulfilledCount++;
results[i] = data;
if (fulfilledCount === count) {
// 当前是最后一个Promise完成了
resolve(results);
}
}, reject);
}
if (count === 0) {
resolve(results);
}
} catch (error) {
reject(error);
console.error(error);
}
});
}
/**
* 等待所有的Promise有结果之后
* 该方法返回的Promise完成
* 并且按照顺序将所有结果汇总
* @param {iterator} proms
*/
static allSettled(proms) {
const ps = [];
for (const p of proms) {
ps.push(
MyPromise.resolve(p).then(
(value) => ({
status: FULFILLED,
value,
}),
(reason) => ({
status: REJECTED,
reason,
})
)
);
}
return MyPromise.all(ps);
}
/**
* 返回的Promise与第一个有结果的一致
* @param {iterator} proms
*/
static race(proms) {
return new MyPromise((resolve, reject) => {
for (const p of proms) {
MyPromise.resolve(p).then(resolve, reject);
}
});
}
}