2020前端面试(十四)- Promise篇

点这里,欢迎关注

Promise

1.解决异步编程的方案:

(1)回调函数:

使用回调函数接受异步任务的执行结果。

缺点:存在回调地狱的问题,必须在启动异步任务之前指定回调函数。

(2)promise:

Promise是异步编程的一种解决方案。Promise对象可以用来封装异步操作,然后保存异步操作的结果。

它不是新的语法功能,只是回调函数的改进,允许将回调函数的嵌套,改成链式调用。

缺点:代码冗余,一大堆的then,原来的语义变得很不清楚。

(3)Generator函数:

通过yield命令将多个异步任务分开,每调用一次next方法,就执行一个异步任务,并返回异步任务的执行结果。

缺点:需要手动调用next方法

(4)async,await :

Generator的封装,相当于一种语法糖,可以自动调用next方法

2.Promise基础

(1)promise的三种状态:

pendding,resolved,rejected

promise的状态只能从pendding状态变为resolved,rejected状态,不能逆向转变,且只能改变一次。

(2)promise的then方法:

用于指定成功的回调函数和失败的回调函数,用于接收异步任务执行的结果,可以为promise对象指定多个成功和失败的回调函数。

(3)then方法返回的Promise对象的状态和值由什么决定:

then方法会返回一个promise对象,该对象的状态由指定的回调函数的返回值决定:

若没有返回值,则返回的Promise的状态为resolved,值为undefined;

若返回的是一个非promise对象的值,则返回的promise的状态为resolved,值为原返回值;

若抛出的是一个异常,则返回的Promise对象的状态为rejected,值为原错误对象。

若返回的是一个promise对象,则then方法返回的promise状态和值与该promise对象保持一致。

3.回调地狱相关

(1)什么是回调地狱问题:

回调地狱是指同时存在多个异步操作,且后面的异步操作依赖于前面操作的结果,形成了回调函数多层嵌套的情况。

回调函数的缺点:不便于阅读,代码维护较困难。

(2)Promise解决回调地狱的问题:

通过链式调用的方法解决回调地狱的问题

new Promise((res,rej)=>{
    setTimeout(()=>{
		res(1111)
    })
}).then(value=>{
    setTimeout(()=>{
        res(222)
    })
}).then(value=>{
    setTimeout(()=>{
        res(333)
    })
})

(3)Generator函数解决回调地狱的问题:

利用Generator函数的暂停执行的效果,可以把异步操作写在yield语句里面,等到调用next方法时再往后执行。

这样做的好处是不需要通过回调函数来拿到异步操作的执行结果。简化了代码。

缺点:需要手动调用next方法

function* fn(){
    yeild 异步任务1;
    yeild (function(rs1){return xxx})(rs1) 异步任务2}
let fn();
let rs1=fn.next();//返回的是异步任务1的结果
fn.next(rs1);//将异步任务1的结果作为参数传递

(4)async/await解决回调地狱的问题:

async/await是generator的封装,async可以自动执行next方法;

async会返回一个promise对象。该对象的状态由async函数的返回值决定。

async function fn(){
    const rs=await fn1();
    const rs=await fn2();
}

4.Promise代码相关:

(1)用es6中的类简单的实现一个promise:

class Promise {
    constructor(executor) {
        this.state = "pendding";
        this.data = undefined;
        this.callBack = [];
        executor(this.resolve.bind(this), this.reject.bind(this));
    }
    resolve(data) {
        if (this.state != "pendding") {
            return;
        }
        this.state = "resolved";
        this.data = data;
        const that = this;
        if (this.callBack.length > 0) {
            setTimeout(() => {
                that.callBack.forEach((item) => {
                    item.onResolved(that.data);
                });
            });
        }
    }
    reject(reason) {
        if (this.state != "pendding") {
            return;
        }
        this.state = "rejected";
        this.data = reason;
        const that = this;
        if (that.callBack.length > 0) {
            setTimeout(() => {
                that.callBack.forEach((item) => {
                    item.onRejected(that.data);
                });
            });
        }
    }
    then(onResolved, onRejected) {
        if (this.state == "resolved") {
            onResolved(this.data);
        } else if (this.state == "rejected") {
            onRejected(this.data);
        } else {
            this.callBack.push({
                onResolved,
                onRejected,
            });
        }
    }
}
new Promise((resolve, reject) => {
    setTimeout(() => {
        // resolve(222);
        reject(333);
    }, 1000);
}).then(
    (value) => {
        console.log(value);
    },
    (reason) => {
        console.log(reason);
    }
);

(2)promise封装Ajax请求:

function myAjax(url){
    return new Promise((resolve,reject)=>{
        const xhr=new XMLHttpRequest();
        xhr.open('get',url);
        xhr.send();
        xhr.onreadystatechange=function(){
            if(xhr.readyState==4){
                if(xhr.status>=200&&xhr.status<300){
                    resolve(xhr.response);
                }else{
                    reject(xhr.status)
                }
            }
        }
    })
}
const p = myAjax('http://....');
p.then(value=>{
    console.log(value);
})

(3)如果已经有三个promise,A、B和C,想串行执行,该怎么写?:

//1.直接利用promise的链式调用
new Promise((res,rej)=>{
    return A;
}).then(value=>{
    return B;
}).then(value=>{
    return C;
})

//2.使用async/await
(async function fn(){
    const rs1=await A;
    const rs2=await B;
    const rs3=await C;
})()

(4)Promise例题:

① 示例1:
setTimeout(() => {
    console.log(1);
})
new Promise((resolve, reject) => {
    console.log(2);
    resolve()
}).then(value => {
    console.log(3);
}).then(value => {
    console.log(4);
})
console.log(5);

输出:2 5 3 4 1

② 示例2:
const first = () => (new Promise((resolve, reject) => {
    console.log(1);
    let p = new Promise((resolve, reject) => {
        console.log(2);
        setTimeout(() => {
            console.log(3);
            resolve(4)
        })
        resolve(5)
    })
    resolve(6)
    p.then(value => {
        console.log(value);
    })
}))

first().then(value => {
    console.log(value);
})
console.log(7);

输出:1 2 7 5 6 3

③ ⭐️ 示例3:
setTimeout(() => {
    console.log(1);
}, 0);
new Promise((resolve, reject) => {
    console.log(2);
    resolve()
}).then(value => {
    console.log(3);
    new Promise((resolve, reject) => {
        console.log(4);
        resolve()
    }).then(value => {
        console.log(5);
    }).then(value => {
        console.log(6);
    })
}).then(value => {
    console.log(7);
})

new Promise((resolve, reject) => {
    console.log(8);
    resolve()
}).then(value => {
    console.log(9);
})

输出:2 8 3 4 9 5 7 6 1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值