Promise是异步操作的一种解决方案
// 1.认识Promise
    document.addEventListener('click',()=>{
        console.log('这里是异步的');
    });
    console.log('这里是同步的'); 
Promise一般用来解决层层嵌套的回调函数(回调地狱)的问题
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style>
        *{
            padding: 0;
            margin: 0;
        }
        #box{
            width: 300px;
            height: 300px;
            background-color: red;
            transition: all .5s;
        }
    </style>
</head>
<body>
<div id="box"></div>
<script>
    // 运动
    const move=(el,{x=0,y=0}={},end=()=>{})=>{
        el.style.transform=`translate3d(${x}px,${y}px,0`;
        el.addEventListener('transitionend',()=>{
            end();
        });
    };
    const boxEl=document.getElementById('box');
    document.addEventListener('click',()=>{
        move(boxEl,{x:150},()=>{
            move(boxEl,{x:150,y:150},()=>{
                move(boxEl,{y:150},()=>{
                    move(boxEl,{x:0,y:0});
                })
            })
        })
    });
</script>
</body>
</html> 

Promise的基本用法
- 实例化构造函数生成实例对象
 - Promise的状态
 
// 1.实例化构造函数生成实例对象
    console.log(Promise);
    // Promise解决的不是回调函数,而是回调地狱
    //const p=new Promise(()=>{});
    // 2.Promise的状态
    const p=new Promise((resolve,reject)=>{
    });
    console.log(p); 
  
Promise有3种状态,一开始是pending(未完成),执行resolve,变成fulfilled(resolved),已成功,执行reject,变成rejected,已失败
// 2.Promise的状态
    const p=new Promise((resolve,reject)=>{
        resolve();
    });
    console.log(p); 

// 2.Promise的状态
    const p=new Promise((resolve,reject)=>{
        // resolve();
        reject();
    });
    console.log(p); 
  
Promise的状态一旦发生变化,就不会再变化了 eg:先调用resolve()再调用reject() 状态从pending->fulfilled 之后虽然执行了reject(),但是状态不变还是fulfilled
// 2.Promise的状态
    const p=new Promise((resolve,reject)=>{
        resolve();
        reject();
    });
    console.log(p); 
 
- then方法
 
// 2.Promise的状态
    const p=new Promise((resolve,reject)=>{
        resolve();
    });
    // 3.then()方法
    // 第一个回调函数代表成功 第二个回调函数代表失败
    p.then(()=>{
        console.log('success');
    },()=>{
        console.log('error');
    }); 
控制台输出success,如果调用reject()方法,则输出error
- resolve和reject函数的参数
 
// 2.Promise的状态
    const p=new Promise((resolve,reject)=>{
        resolve({username:'jessica'});
        //reject(new Error('reason'));
    });
    // 3.then()方法
    // 第一个回调函数代表成功 第二个回调函数代表失败
    // 4.resolve()和reject()参数
    p.then((data)=>{
        console.log('success',data);
    },(err)=>{
        console.log('error',err);
    }); 

Promise的实例方法
then()返回的是一个新的Promise对象
- 什么时候执行
 - 执行后的返回值
 - then方法返回的Promise对象的状态改变(return后面的东西会用Promise包装一下)
 
const p=new Promise((resolve,reject)=>{
        // 第一次调用resolve方法必须写 后面新的Promise对象可不写 
        resolve();
    });
    p.then(()=>{
        console.log('success1');
        // 默认返回的是 undefined
        // 默认返回的是成功状态的Promise对象
        // 不传参数的话可以不写return语句 最方便
        // return undefined;
        // 传参数的话直接return+参数 方便
        // return 123;
        // 也可以用下面的形式 源代码就是这样
        return new Promise((resolve,reject)=>{
            // resolve(undefined);
            // resolve(123);
            reject('reason');
        })
    },()=>{
        console.log('error1');
    }).then(data=>{
        console.log('success2',data);
    },err=>{
        console.log('error2',err);
    }) 
 
- 向后传值
 - 使用Promise解决回调地狱
 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style>
        * {
            padding: 0;
            margin: 0;
        }
        #box {
            width: 300px;
            height: 300px;
            background-color: red;
            transition: all .5s;
        }
    </style>
</head>
<body>
<div id="box"></div>
<script>
    // 运动
    const move = (el, {x = 0, y = 0} = {}, end = () => {
    }) => {
        el.style.transform = `translate3d(${x}px,${y}px,0`;
        el.addEventListener('transitionend', () => {
            end();
        });
    };
    const boxEl = document.getElementById('box');
    const movePromise = (el, point) => {
        return new Promise(resolve => {
            move(el, point, () => {
                resolve();
            });
        });
    };
    document.addEventListener('click', () => {
        movePromise(boxEl, {x: 150})
            .then(() => {
                return movePromise(boxEl, {
                    x: 150, y: 150
                });
            })
            .then(() => {
                return movePromise(boxEl, {
                    y: 150
                });
            })
            .then(() => {
                return movePromise(boxEl, {x: 0, y: 0});
            });
    }, false);
</script>
</body>
</html> 
 
catch()方法专门用来处理rejected状态
catch()可以捕获它前面的错误,一般总是建议,Promise对象后面要跟catch方法,这样可以处理Promise内部发生的错误
const p=new Promise((resolve,reject)=>{
        //resolve();
        reject('reason');
    });
    // 第一个then只有一个箭头函数,但是由于上面是失败状态
    // 只能通过下一次then继续寻找第二个箭头函数,如若找不到以此类推寻找
    // p.then(data=>{
    //     console.log(data);
    // }).then(null,err=>{
    //     console.log(err);
    // });
    // catch是then的特例
    // 专门捕获失败时状态
    p.then(data=>{
        console.log(data);
    }).catch(err=>{
        console.log(err);
        //默认返回undefined,成功状态的Promise对象
        // 可以制造出失败时错误
        throw new Error('reason');
    }).then(data=>{
        console.log(data);
    }) 

finally()方法
- 什么时候执行
 - 当Promise状态发生变化时,不论如何变化都会执行,不变化不执行
 
// finally一般不接收参数
    new Promise((resolve,reject)=>{
        // resolve(123);
        reject('reason');
    }).finally(data=>{
        console.log(data);
    }).catch(err=>{
        console.log(err);
    }) 
 ![]()
构造函数方法
Promise.resolve()和Promise.reject()
当Promise.resolve()的参数接收的是Promise对象时,那么直接返回Promise对象,什么都不做
const p1=new Promise(resolve => {
        setTimeout(resolve,1000,'我执行了');
        // 定时器第三个参数相当于以下这种形式
        setTimeout(()=>{
            resolve('我执行了')
        },1000);
    });
    // 当Promise.resolve()接收的是Promise对象时,
    // 直接返回这个Promise对象,什么都不做
    Promise.resolve(p1).then(data=>{
        console.log(data);
    })
    // 等价于
    // p1.then(data=>{
    //     console.log(data);
    // })
    console.log(Promise.resolve(p1) === p1);//true 
控制台隔了1s后才输出我执行了
当resolve函数接收的是Promise对象时,后面的then会根据传递的Promise对象的状态变化决定执行哪一个回调
const p1 = new Promise(resolve => {
        setTimeout(resolve, 1000, '我执行了');
        // 定时器第三个参数相当于以下这种形式
        setTimeout(() => {
            resolve('我执行了')
        }, 1000);
    });
    new Promise(resolve => resolve(p1)).then(data=>{
        console.log(data);
    }) 
当resolve函数接收的是具有then()方法的对象时
const thenable={
        then(){
            console.log('then');
        }
    };
    Promise.resolve(thenable).then(
        data=>console.log(data),
        err=>console.log(err));
    console.log(Promise.resolve(thenable)); 
pending状态,所以回调一个都不会执行
要执行回调,需要以下操作
const thenable={
        then(resolve,reject){
            console.log('then');
            resolve('resolve');
        }
    };
    Promise.resolve(thenable).then(
        data=>console.log(data),
        err=>console.log(err));
    console.log(Promise.resolve(thenable)); 
 
reject函数:不管什么参数,都会原封不动地向后传递作为后续方法的参数
const p1 = new Promise(resolve => {
        setTimeout(resolve, 1000, '我执行了');
        // 定时器第三个参数相当于以下这种形式
        // setTimeout(() => {
        //     resolve('我执行了')
        // }, 1000);
    });
    // new Promise((resolve,reject)=>{
    //     reject('reason');
    // })
    // // 简写形式
    // Promise.reject('reason');
    Promise.reject(p1).catch(
        err => console.log(err)
    ) 
 ![]()
Promise.all()
- 关注多个Promise对象的状态变化 传入多个Promise实例,包装成一个新的Promise实例返回
 - Promise.all()的状态变化与所有传入的Promise实例对象状态有关
 - 所有状态都变成resolved,最终的状态才会变成resolved
 - 只要有一个变成rejected,最终的状态就变成rejected
 
const delay=ms=>{
        return new Promise(resolve => {
            setTimeout(resolve,ms);
        })
    };
    const p1=delay(1000).then(()=>{
        console.log('p1 完成了');
        return 'p1';
    });
    const p2=delay(2000).then(()=>{
        console.log('p2 完成了');
        return 'p2';
        // return Promise.reject('reason');
    });
    const p=Promise.all([p1,p2]);
    p.then(data=>{
        console.log(data);
    },err=>{
        console.log(err);
    }) 

Promise.race():竞赛永争第一
- Promise.race()的状态取决于第一个完成的Promise实例对象,如果第一个完成的成功了,那最终就成功;如果第一个完成的失败了,那最终就失败
 
const delay=ms=>{
        return new Promise(resolve => {
            setTimeout(resolve,ms);
        })
    };
    const p1=delay(1000).then(()=>{
        console.log('p1 完成了');
        // return 'p1';
        return Promise.reject('reason');
    });
    const p2=delay(2000).then(()=>{
        console.log('p2 完成了');
        return 'p2';
    });
    const racePromise=Promise.race([p1,p2]);
    racePromise.then(data=>{
        console.log(data);
    },err=>{
        console.log(err);
    }) 
 
Promise.allSettled()
- Promise.allSettled()的状态与传入的Promise状态无关,永远都是成功的
 
const delay=ms=>{
        return new Promise(resolve => {
            setTimeout(resolve,ms);
        })
    };
    const p1=delay(1000).then(()=>{
        console.log('p1 完成了');
        return 'p1';
        //return Promise.reject('reason');
    });
    const p2=delay(2000).then(()=>{
        console.log('p2 完成了');
        return 'p2';
    });
    const allSettledPromise=Promise.allSettled([p1,p2]);
    allSettledPromise.then(data=>{
        console.log(data);
    }); 
 
Promise的注意事项
- resolve或reject函数执行后的代码(推荐在调用其函数时加上return,不再执行它们后面的代码)
 - Promise.all/race/allSettled的参数问题
 - 参数如果不是Promise数组,会将不是Promise的数组元素转变成Promise对象
 
Promise.all([1,2,3]).then(datas=>{
        console.log(datas);
    })
    // 相当于
    Promise.all([
        Promise.resolve(1),
        Promise.resolve(2),
        Promise.resolve(3)]
    ).then(datas=>{
        console.log(datas);
    }) 
 
- 不只是数组,任何可遍历的都可以作为参数(数组、字符串、Set、Map、NodeList、arguments)
 
Promise.all(new Set([1,2,3])).then(datas=>{
        console.log(datas);
    }) 
 
- Promise.all/race/allSettled的错误处理
 
Promise的应用
异步加载图片
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <style>
        #img {
            width: 80%;
            padding: 10%;
        }
    </style>
</head>
<body>
<img src="https://img.mukewang.com/5e6af63d00011da318720764.jpg" alt="" id="img" >
<script>
    // 异步加载图片
    const loadImgAsync = url => {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => {
                resolve(img);
            }
            img.onerror = () => {
                reject(new Error(`Could not load image at ${url}`));
            }
            img.src = url;
        });
    };
    const imgDOM = document.getElementById('img');
    loadImgAsync('https://2img.mukewang.com/5f057a6a0001f4f918720764.jpg')
        .then(img => {
            console.log(img.src);
            setTimeout(() => {
                imgDOM.src = img.src;
            }, 1000);
        })
        .catch(err => {
            console.log(err);
        });
</script>
</body>
</html> 
                









![[ 数据结构 ] 图(Graph)--------深度优先、广度优先遍历](https://img-blog.csdnimg.cn/img_convert/b007c0fc4996d3f0def1780c007b3851.png)







