Promise

1.为什么要使用promise?

(1) 解决回调地狱(Callback Hell)

回调地狱:回调函数嵌套调用, 外部回调函数异步执行的结果是嵌套的回调函数执行的条件。

缺点:会产生回调的嵌套,代码组织形式也是逐渐向右,这种代码是难以阅读的。然后就是异常处理还要分别去处理,这样也会加大处理难度。

(2) 指定回调方式更灵活

纯回调:回调函数必须事先指定好,也就是要先指定回调函数,然后再启动异步任务,而且必须这样。

promise:回调函数可以在启动异步任务后指定,也可以在启动异步任务之前。甚至可以在异步任务完成后指定,依然能够得到结果数据。

2.promise实践

(1) promsie初体验

    <div id="btn">点我抽奖</div>
    <script>
        const random = () => {
            return Math.floor(Math.random() * 100 + 1);  //0-100随机数
        }
        const btn = document.getElementById('btn');
        btn.addEventListener('click', () => {
            const p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    let num = random();
                    if (num < 50) {    // 50%中奖率
                        resolve(num);
                    } else {
                        reject(num);
                    }
                }, 1000)
            });
            p.then(res => {
                alert('中奖了' + res);
            }, reason => {
                alert('感谢参加此次活动' + reason);
            })
        });
    </script>

(2) promise封装Ajax请求

    <div id="btn">点我发送Ajax</div>
    <script>
        const btn = document.getElementById('btn');
        btn.addEventListener('click', () => {
            const p = new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest();
                xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts');  //接口源自网络
                xhr.send();
                xhr.onreadystatechange = () => {
                    if (xhr.readyState === 4) {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            resolve(xhr.response);
                        } else {
                            reject(xhr.status);
                        }
                    }
                }
            });
            p.then(res => {
                console.log(res);
            }, reason => {
                console.log(reason);
            })
        });
    </script>

(3) 封装一个函数读取文件内容

const mineReadFile = path => {
    return new Promise((resolve, reject) => {
        require('fs').readFile(path, (err, data) => {
            if (err) reject(err);
            resolve(data);
        })
    })
}
//使用
mineReadFile('./text/index.txt').then(res => {
    console.log(res.toString());
}, reason => {
    console.log(reason);
})

(4) 封装一个函数发送 GET ajax请求

        const sendAJAX = url => {
            return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest();
                xhr.open("GET", url);
                xhr.send();
                xhr.onreadystatechange = () => {
                    if (xhr.readyState === 4) {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            resolve(xhr.response);
                        } else {
                            reject(xhr.status);
                        }
                    }
                }
            })
        }
        sendAJAX('https://jsonplaceholder.typicode.com/posts').then(res => {
            console.log(res);
        }, reason => {
            console.log(reason);
        })

3.promise状态

3种状态,开始为pending(未完成),执行resolve(),变成fulfilled/resolved(已成功);执行reject(),变成rejected,已失败。

注意:一个promise对象只能从pending变为fulfilled/resolved或从pending变为rejected,且只改变一次,无论成功或失败都有一个结果数据,且结果数据只能被resolve()或reject()修改。

4.promise基本流程

 5.promise的api

1.promise构造函数:Promise(excutor){}

(1)executor函数:执行器(resolve,reject)=>{}

(2)resolve函数:内部定义成功时调用的函数

(3)reject函数:内部定义失败时调用的函数

注意:excutor会在Promise内部立即同步调用

2.Promise.prototype.then()方法:(onResolved,onRejected)=>{}

(1)onResolved函数:成功的回调函数 res=>{}

(2)onRejected函数:失败的回调函数 reason=>{}

说明:指定用于得到成功的res的成功回调和用于得到失败的reason的失败回调,返回一个新的promise对象

3.Promise.prototype.catch()方法:(onRejected)=>{}

(1)onRejected函数:失败的回调函数 reason=>{}

说明:本质是then()的特例,专门处理失败状态;then()可处理成功和失败状态,专门用来处理成功状态。

4.Promise.resolve() 

new Promise(resolve => resolve());
Promise.resolve();   //两者等价,后者相当于Promise成功状态的简写形式

说明:对于Promise.resolve()的参数,若为非promise类型的对象,则返回成功状态下的promise对象。若传入参数为promise对象,则返回的promise对象状态由参数(传入的promise对象的结果)决定。

5.Promise.reject() 

new Promise(reject => reject());
Promise.reject();   //两者等价,后者相当于Promise失败状态的简写形式

说明:它的参数,不管什么类型,都会原封不动的向后传递,作为后续方法的参数。且返回值为一个失败的promise对象。

6.Promise.all() 

const p1 = new Promise(resolve =>{
    resolve('p1:success');
})
const p2 = Promise.resolve('p2:success');
const p3 = Promise.resolve('p3:success');
const result = Promise.all([p1,p2,p3]);
console.log(result);

const p1 = new Promise((resolve,reject) =>{
    reject('p1:failed');
})
const p2 = Promise.resolve('p2:success');
const p3 = Promise.resolve('p3:success');
const result = Promise.all([p1,p2,p3]);
console.log(result);

 

 说明:传入多个promise实例,包装成一个新的promise对象返回;只有传入的promise都成功才成功,且返回成功结果组成的数组,只要有一个失败,就失败,返回失败的promise对象的结果。

7.Promise.race() 

const p1 = new Promise((resolve,reject) =>{
    setTimeout(function(){
        reject('p1:failed');
    })
})
const p2 = Promise.resolve('p2:success');
const p3 = Promise.resolve('p3:success');
const result = Promise.race([p1,p2,p3]);
console.log(result);

说明:Promise.race()状态取决于最先完成的promise状态,最先完成的成功了,就成功;否则,就失败。

8.Promise.allSettled() 

const p1 = new Promise((resolve,reject) =>{
    setTimeout(function(){
        reject('p1:failed');
    })
})
const p2 = Promise.resolve('p2:success');
const p3 = Promise.resolve('p3:success');
const result = Promise.allSettled([p1,p2,p3]);
console.log(result);

说明:Promise.allSettled()的状态与传入的promise状态无关,永远都是成功的,它会忠实的记录下各个promise的表现。 

6.promise面试相关问题

1.如何改变promise的状态?

const p1 = new Promise((resolve,reject) =>{
       resolve('成功了');  //1.pending ——>fulfilled/resolved
       reject('失败了');   //2.pending ——>rejected
       throw('出错了');  //3.pending ——>rejected
})

2.一个promise指定多个成功/失败回调函数,都会调用吗?

当promise改变为对应状态时都会调用。

const p1 = new Promise((resolve, reject) => {
    resolve('成功了');
})
p1.then(res => {
    console.log('then1调用了');
})
p1.then(res => {
    console.log('then2调用了');
})
//then1调用了   then2调用了 

3.改变promise状态和指定回调函数,执行谁先谁后?

都有可能,如下:

(1)先改变状态,再指定回调

const p1 = new Promise((resolve, reject) => {
    resolve('成功了');   //在执行器中直接定义了resolve()/reject()
})
p1.then(res => {
    console.log(res);
})
const p1 = new Promise((resolve, reject) => {
    resolve('成功了');    
})
setTimeout(function () {   //延长更长时间才调用then
    p1.then(res => {
        console.log(res);
    })
},1000)

(2)先指定回调,再改变状态(常用)

const p1 = new Promise((resolve, reject) => {
    setTimeout(function(){
        resolve('成功了');
    },1000)    
})
p1.then(res => {
    console.log(res);
})

4.promise.then()返回新的promise的结果状态由什么决定?

(1) 抛出异常:新promise状态为rejected

const p1 = new Promise((resolve, reject) => {
        resolve('成功了');
})
const result = p1.then(res => {
     throw '出错了';
})
console.log(result);

(2)非promise的任意值:新promise状态为fulfilled

const p1 = new Promise((resolve, reject) => {
        resolve('成功了');
})
const result = p1.then(res => {
      return 123
})
console.log(result);

(3) promise:新promise状态由返回的promise状态决定

const p1 = new Promise((resolve, reject) => {
        resolve('成功了');
})
const result = p1.then(res => {
      return new Promise((resolve,rejected)=>{
          resolve('success');
      })
})
console.log(result);  //fulfilled

5.promise如何串联多个操作任务?

通过then()进行链式调用

const p1 = new Promise((resolve, reject) => {
    resolve('1');
})
const result = p1.then(res => {
    return new Promise((resolve, reject) => {
        resolve('2');
    })
}).then(res=>{
    console.log(res);  //2
}).then(res=>{
    console.log(res);  //undefined
})
console.log(result);

undefined原因:它的上一层then()没有传递参数;

fulfilled原因:undefined属于非promise对象的任意值,故成功状态。

6.异常穿透?

当使用promise的then()链式调用时,可以在最后指定失败的回调,当前面的任何操作出现异常,都会传到最后失败的回调中处理。

const p1 = new Promise((resolve, reject) => {
    resolve();
})
p1.then(res => {
    return new Promise((resolve, reject) => {
        resolve();
    })
}).then(res => {
    throw "中间出错了"
}).then(res => {
    console.log(2);
}).catch(reason => {
    console.log(reason);   //中间出错了
})

7.中断promise链?

当使用promise的then的链式调用时,在中间中断,不在调用后面的回调函数。

方法:返回一个pending状态的promise对象。

const p1 = new Promise((resolve, reject) => {
    resolve();
})
p1.then(res => {
    return new Promise((resolve, reject) => {
        resolve('1');
    })
}).then(res => {
     console.log(res);
     return new Promise(()=>{});
}).then(res => {
    console.log(2);
}).then(res => {
    console.log(3);
})
//结果为1,因为被pending状态的promise对象中断了

7.async与await

1.async函数

(1)函数的返回值为promise对象;

(2)promise对象的结果由async函数执行的返回值决定。

说明:(2)与 4.promise.then()返回新的promise的结果状态由什么决定? 一摸一样。如·:

 async function fn(){
     throw "error";    
 }
 let result = fn();
 console.log(result);

 2.await表达式

(1)await右侧的表达式一般为promise对象,但是也可以是其它值;

(2)如果表达式是promise对象,await返回的是promise成功的值;

(3)如果表达式是其他值,直接将此值作为await的返回值。

说明:await必须写在async函数中,但async函数中可以没有await;

如果await的promise失败了,就会抛出异常,需要通过try...catch捕获处理;

 async function fn(){
     let p = new Promise((resolve,reject)=>{
         resolve('success');
     })
     let result = await p;  //await右侧的表达式为promise对象 
     console.log(result);
 }
 fn();  //success
 async function fn(){
     let result = await 123;  //await右侧的表达式是其他值
     console.log(result);
 }
 fn();  //123
async function fn() {
    let p = new Promise((resolve, reject) => {
        reject('failed');
    })
    try {
        let result = await p;
    } catch (e) {
        console.log(e);
    }
}
fn();  //failed

3.实践

Ajax请求

    <div id="btn">点我发送Ajax</div>
    <script>
        const sendAJAX = url => {
            return new Promise((resolve, reject) => {
                const xhr = new XMLHttpRequest();
                xhr.open("GET", url);
                xhr.send();
                xhr.onreadystatechange = () => {
                    if (xhr.readyState === 4) {
                        if (xhr.status >= 200 && xhr.status < 300) {
                            resolve(xhr.response);
                        } else {
                            reject(xhr.status);
                        }
                    }
                }
            })
        }
        const btn = document.getElementById('btn');
        btn.addEventListener('click', async function () {
            let result = await sendAJAX('https://jsonplaceholder.typicode.com/posts');
            console.log(result);
        })
    </script>

总结:

1. 是比promise更高级处理回调地狱的方案,无需书写回调函数。

2. async/await 可以使用的 try/catch 做错误处理,比 Promise 的错误捕获更简洁。

3. 代码阅读性友好。

面试题:

手写promise加载图片

    <img src="" alt="" id="img">
    <script>
        const loadImgAsync = url => {
            return new Promise((resolve, reject) => {
                const img = new Image();
                img.onload = () => {
                    resolve(img);
                }
                img.onerror = () => {
                    reject(`Could not load image at${url}`);
                }
                img.src = url;
            })
        }
        const imgdom = document.getElementById('img');
        loadImgAsync('https://s3.bmp.ovh/imgs/2022/05/01/b54d18ea20b08e8b.png').then(img => {
            setTimeout(() => {
                imgdom.src = img.src;
            }, 1000)
        }).catch(err => {
            console.log(err)
        })
    </script>

文章大部分内容来自:

尚硅谷Web前端Promise教程从入门到精通_哔哩哔哩_bilibili

建议去观看,老师讲的很好!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值