一、为什么要引入Promise
- Promise解决了什么问题?
- Promise有哪些具体的使用场景?
Promise解决了什么问题?
- 回调地狱问题
// 回调地狱实例
// 奶茶函数
function getTea(fn) {
setTimeout(() => {
fn('获取到一杯奶茶')
},2000)
}
// 面包函数
function getBread(fn) {
setTimeout(() => {
fn('获取到一个面包')
},100)
}
// 如果必须按照顺序获取,而不是根据时间,要求是先获取到奶茶后获取到面包。
getTea(function(data) {
console.log(data);
getBread(function(data) {
console.log(data);
})
})
- 可读性问题
// 下面解释下,如何通过Promise来解决回调地狱的问题
function getTea() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('获取到一杯奶茶')
}, 2000)
})
}
function getBread() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('获取到一个面包')
}, 500)
})
}
getTea()
.then(res => {
console.log(res);
return getBread();
})
.then(res => {
console.log(res);
})
- 信任问题(也叫回调多次执行问题)
Promise有哪些具体的使用场景?
- 场景1:将图片的加载写成一个Promise,图片一旦加载完成,Promise的状态就会发生变化。
- 场景2:当下一个异步请求需要依赖上一个请求结果的时候,可以通过链式操作解决问题。
- 场景3:通过all()实现多个请求合并在一起,汇总所有的请求结果,只需设置一个loading即可。
- 场景4:通过race()可以设置图片请求超时。
二、手写Prromise身上的方法
手写Promise.all
function myPromiseAll(iterable) {
// 首先明确要返回的对象是一个Promise
return new Promise((resolve,reject) => {
// 首先将可迭代对象转换为数组
const promises = Array.from(iterable);
let flag = 0;
const result = [];
// 开始遍历执行
for (let i = 0; i < promises.length; i++) {
Promise.resolve(promises[i]).then(res => {
result[i] = res;
flag++;
if (flag === promises.length) {
resolve(result)
}
}).catch(err => {
reject(err)
})
}
})
}
手写Promise.race
// 手写promise.race
function myPromiseRace(iterator) {
// 首先返回的是一个promise对象
return new Promise((resolve,reject) => {
for (let item of iterator) {
Promise.resolve(item).then(res => {
resolve(item);
}).catch(err => {
reject(err);
})
}
})
}
let p1 = new Promise(resolve => {
setTimeout(resolve, 105, 'p1 done')
})
let p2 = new Promise(resolve => {
setTimeout(resolve, 100, 'p2 done')
})
myPromiseRace([p1, p2]).then(data => {
console.log(data); // p2 done
})
手写Promise.finally
- 无论成功还是失败,都会执行这个方法
- 返回的是一个Promise
let p = new Promise((resolve,reject) => {
setTimeout(() => {
resolve(111);
},2000)
})
p.then(res => {
console.log(res); // 111
}).finally(() => {
console.log('无论如何这里都会被执行'); // 无论如何这里都会被执行
})
Promise.prototype.finally = function(f) {
return this.then((value) => {
return Promise.resolve(f()).then(() => value)
},(err) => {
return Promise.resolve(f()).then(() => {
throw err;
})
})
}
Promise.all和Promise.race的区别
Promise.all和Promise.race的应用场景
promise.all()的应用场景
- 多个异步任务都得到结果时,进行显示的场景
Promise.race()的应用场景
- 提示用户请求超时
三、Promise是如何解决串行和并行的?
什么是并行?什么是串行?
Promise实现并行请求
Promise实现串行请求
// 借助reduce函数来实现Promise的串行执行
const funcArr = [
() => {
return new Promise((resolve) => {
setTimeout(() => {resolve(1)},2000)
})
},
() => {
return new Promise((resolve) => {
setTimeout(() => {resolve(2)},1000)
})
},
() => {
return new Promise((resolve) => {
setTimeout(() => {resolve(3)},3000)
})
},
];
function inOrder(arr) {
const res = [];
return new Promise((resolve) => {
arr.reduce((pre,cur) => {
return pre.then(cur).then(data => res.push(data))
},Promise.resolve()).then(data => resolve(res))
})
}
inOrder(funcArr).then(data => console.log(data)) // [1,2,3]
四、什么是Promise穿透?
Promise.resolve(1)
.then(function(){return 2})
.then(Promise.resolve(3))
.then(console.log) // 2
五、使用Promise封装Ajax请求
// 使用Promise封装Ajax请求
const res = new Promise((resolve,reject) => {
// 1. 创建一个XMLHttpRequest对象
const xhr = new XMLHttpRequest();
// 2. 初始化请求方法和URL
xhr.open('GET','https://api.apiopen.top/getJoke');
// 3. 发送请求
xhr.send();
// 4. 绑定事件,处理响应结果
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
// 这里4代表的就是说服务端返回了全部的结果
// 如果服务端返回的状态码是2开头的,我们就resolve这个返回的结果,反之则reject对应的状态码
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
res.then(function(value) {
console.log(value);
},function(err) {
console.log(err);
})
六、Promise有哪些状态?
- pending状态(初始状态)
- fulfilled状态(已经成功的状态)
- rejected状态(已经失败的状态)
Promise状态的变化过程
- 从pending到fulfilled状态的切换
const p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('resolve前的状态:', p);
resolve();
console.log('resolve之后的状态', p);
})
})
- 从pending状态到rejected状态
const p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('reject前的状态:', p);
reject();
console.log('reject之后的状态', p);
})
})
七、将callback改写成Promise
-
传统callback的形式
const fs = require(‘fs’);
fs.readFile(’./temp.md’,(err,data) => {
console.log(data.toString());
}) -
将callback改为promise的形式
const fs = require('fs');
async function myReadFile() {
let result = await new Promise((resolve,reject) => {
fs.readFile('./temp.md',(err,data) => {
resolve(data.toString());
})
})
console.log(result); // xxxxx
return result;
}
myReadFile()