/**
* 浅谈 Promise
* 问题:怎么理解的?:Promise是什么,做什么的,有啥问题,有啥解决办法
*/
/**
* 1、Promise是一种异步编程的解决方案,用于解决回调地狱的问题
* 是构造函数,可以理解为是一个容器,里面是异步事件;
*/
/**
* 2、
* 特点:
* 1、Promise对象的状态不受外界影响,三种状态:pending(初始状态)、fulfilled(成功)、rejected(失败);
* 2、状态改变后不再改变,状态不可逆,只能pending到fulfilled,或者pending变成rejected;
* 缺点:
* 1、无法取消Promise,一旦建立无法取消;
* 2、如果不设置回调函数,Promise内容出错,不会反映到外部;
* 3、处于pending状态,无法得知是处于了哪一个阶段,是刚开始,还是快结束了;
*/
/**
* 3、Promise的方法 && 简单实现 && axios的实现
* Promise.then()、Promise.catch()、Promise.finally()
*/
// 1、Promise.resolve(value)方法返回一个以给定值解析后的Promise 对象
Promise.resolve('success').then((res) => {
console.log(res); // success
}, (res) => {
// 不会被调用
})
// 2、Promise.reject(reason)方法返回一个带有拒绝原因reason参数的Promise对象
Promise.reject('failed').then((res) => {
// 不会被调用
},(res) => {
console.log(res); // failed
})
// 3、Promise.race(iterable)方法返回一个promise,一旦迭代器中的某个promise解决或者拒绝,返回的promise就会解决或者拒绝
let p1 = new Promise((resolve, reject) => { setTimeout(()=>{resolve('one')}, 1000) })
let p2 = new Promise((resolve, reject) => { setTimeout(()=>{resolve('two')}, 100) })
Promise.race([p1, p2]).then((res) => { console.log(res) });
// two , p2先执行
// 4、Promise.all()方法返回一个Promise实例,接受一个数组(里面都是Promise实例)作为参数,等待所有都成功才会返回成功,或者其中有一个失败则返回失败,且返回该失败的回调;
Promise.all = (promise) => {
return new Promise((resolve, reject) => {
let result = [], index = 0;
if(promise.length === 0){
resolve(result);
}else{
for (let i = 0; i < promise.length; i++) {
Promise.resolve(promise[i]).then((res) => {
result[i] = res;
// 因为then是异步任务,所以需要另加index
if(++index === promise.length) resolve(result);
}, error => {
reject(error);
return;
})
}
}
})
}
/**
* Promise 实现 Polyfill
*/
class Rpromise {
constructor(excutor) {
this.status = 'pending'; // 实例状态
this.fulfilledCallbacks = []; // 成功状态事件池
this.rejectedCallbacks = []; // 失败状态事件池
this.value = undefined; // 记录resolve和reject时候的参数
const resolve = (result) => {
// 如果不是pending那么状态已经不能更改了
if(this.status === 'pending'){
this.status = 'resolved';
this.value = result;
// 为了避免还没有执行then,成功状态事件池还没有添加这里就同步执行了,因为forEach是同步方法
// 所以需要把循环事件池处理成异步的,考虑到then也是异步的,所以不仅要将这里处理成异步的
// 还要这里的异步在then后执行,then是微任务,加定时器变成宏任务,在then后执行
let time = setTimeout(() => {
clearTimeout(time);
// 循环执行成功状态事件池中的事件
this.fulfilledCallbacks.forEach(item => item(this.value));
}, 0);
}
}
const reject = (res) => {
if(this.status === 'pending'){
this.status = 'rejected';
this.value = res;
let time = setTimeout(() => {
clearTimeout(time);
this.rejectedCallbacks.forEach(item => item(this.value));
}, 0);
}
}
try {
excutor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected){
// 需要链式调用then方法,所以then方法的返回值必须是一个promise的实例,而resolve和reject
// 两个形参则是用来改变返回的这个promise的状态的,因为下一个then中的回调还挂着返回这个promise
// 对象的身上,需要通过控制resolve和reject的执行来控制下一个then中执行的方法
return new Rpromise((resolve, reject) => {
if(this.status === 'resolved'){
// 向成功状态事件池添加回调,只添加并没有执行,this指向上一个promise实例,
// 什么时候执行?在调用this的resolve方法时被执行,result就是传进来的this.value
this.fulfilledCallbacks.push((result) => {
// 如果then中的方法执行时报错,直接执行返回的promise的reject方法触发下一个then的失败回调执行
try {
let res = onFulfilled(result);
if(res instanceof Rpromise){
res.then(resolve, reject);
}else{
resolve(res);
}
} catch (error) {
reject(error);
}
})
}
if(this.status === 'rejected'){
this.rejectedCallbacks.push((result) => {
try {
let res = onRejected(result);
if(res instanceof Rpromise){
res.then(resolve, reject);
}else{
reject(res)
}
} catch (error) {
reject(error);
}
})
}
})
}
}
/**
* 用Promise实现 axios
* 1、创建XMLHttpRequest对象
* 2、使用open函数
* 3、设置onreadystatechange回调
* 4、send请求数据
*/
function axios(params) {
const {url, method, data} = params;
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onreadystatechange = () => {
if(xhr.readyState !== 4) return;
if(xhr.statue === 200){
resolve(xhr.response);
}else{
reject(xhr.statusText);
}
}
xhr.send(data);
})
}
/**
* 4、Promise的痛点解决
* 1、无法取消Promise, 使用async/await 可以return终止
* 2、不设置回调出错不会反映到外部,使用async/await 可以每个阶段return Promise.reject(new Error('拒绝'))
*/
// async声明的函数的返回本质上是一个Promise
// async是一个关键字,表示异步
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, "finish");
});
}
timeout(2000).then(value => {
console.log("第一层" + value); return timeout(2000);
}).then(value => {
console.log("第二层" + value); return timeout(2000);
}).then(value => {
console.log("第三层" + value); return timeout(2000);
}).then(value => {
console.log("第四层" + value); return timeout(2000);
}).then(value => {
console.log("第五层" + value); return timeout(2000);
}).catch(err => {
console.log(err);
});
// 改用 async
async function asyncTimeSys(){
await timeout(1000);
console.log("第一层异步结束!")
await timeout(1000);
console.log("第二层异步结束!")
await timeout(1000);
console.log("第三层异步结束!")
await timeout(1000);
console.log("第四层异步结束!")
await timeout(1000);
console.log("第五层异步结束!")
return "all finish";
}
asyncTimeSys().then((value)=>{
console.log(value);
});
// 误区 :await并不是是把同步变为异步
// async 函数返回一个 Promise 对象,当函数执行的时候,一旦遇到 await 就会先返回,
// 等到触发的异步操作完成,再接着执行函数体内后面的语句
async function async1(){
console.log('async1 start')
await async2();
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
async1();
console.log('i am koala')
console.log(timeout())
// async1 start
// async2
// i am koala
// async1 end
// 简单的说,先去执行后面的同步任务代码,执行完成后,
// 也就是表达式中的 Promise 解析完成后继续执行 async 函数并返回解决结果