zhuanghaixin / Interview

8 stars 0 forks source link

[js/异步]Promise #82

Open zhuanghaixin opened 4 years ago

zhuanghaixin commented 4 years ago

Promise

为什么需要Promise?

(解决回调地狱) 因为在js中有大量的回调函数,而回调函数之中往往会再次嵌套其他的回调函数,回调函数嵌套的层数过多,会造成代码的可读性降低,影响代码的维护和扩展。像这样的代码叫回调地狱 #39 。

结论

1.Promise本身没有修改异步的顺序,通过then控制异步结果的顺序从而控制顺序 2.Promise如何让异步有序执行,在上一个promise实例的then方法中返回下一个promise.

Promise的实例方法 then() ,catch()

不带参数


// 不传参数
//1. promise精髓在于状态管理
//1.1 resolve 表示当前异步操作执行成功的一个回调函数
//1.2 reject 表示当前异步操作执行失败的一个回调函数
let p=new Promise((resolve,reject)=>{
setTimeout(()=>{
console.log('执行成功')
if(状态==成功){
resolve() //进入 then()中的第一个函数里面
}else{
reject() //进入 then()中第二个函数里面
}
},1000)

}).then(()=>{ // 2.then(x,y)可以接受两个参数,第一个参数是必须的,第二个参数可以省略 //2.1 第一个异步操作执行成功,会进到第一个参数的函数里面 console.log('成功') },()=>{ //2.2 第一个个异步操作执行成功,会进到第二个参数的函数里面 console.log('失败') })

#### 传参数
```js
// 传参数
//1. promise精髓在于状态管理
//1.1 resolve 表示当前异步操作执行成功的一个回调函数
//1.2 reject 表示当前异步操作执行失败的一个回调函数
let p=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        console.log('执行成功')

            resolve('成功') //进入 then()中的第一个函数里面,将'成功'作为实参传给then()的一个函数

            // reject('失败') //进入 then()中第二个函数里面,将'失败'作为实参传给then()的二个函数

    },1000)
}).then(res=>{   // 2.then(x,y)可以接受两个参数,第一个参数是必须的,第二个参数可以省略
//2.1 第一个异步操作执行成功,会进到第一个参数的函数里面
    console.log(res)  //'成功'
},(res)=>{
//2.2 第一个个异步操作执行成功,会进到第二个参数的函数里面
    console.log(res)  //'失败'
}

注意

第一个例子Promise代码还会马上执行

let p=new Promise((resolve,reject)=>{
    console.log(1)
})

console.log(2)

//输出
// 1
//2

第二个例子 微任务 ,then()里面的

//第二个例子 带有resolve 异步在最后
let p=new Promise((resolve,reject)=>{
    console.log(1)
    resolve(3)
})

setTimeout(()=>{
    console.log(4)
},0)

p.then(res=>{   //then() 里面相当于回调函数
    console.log(res)
})

console.log(2)

//输出 1,2,3,4

第三个例子


//第三个例子 带有resolve  同时还有异步函数
let p=new Promise((resolve,reject)=>{
    console.log(1)
    resolve(3)
})

p.then(res=>{   //then() 里面相当于回调函数
    setTimeout(()=>{
        console.log(res)
    },0)
})
setTimeout(()=>{
    console.log(4)
},0)

console.log(2)

//输出 1,2,4,3

宏任务与微任务

依据我们多年编写 ajax 的经验:js 应该是按照语句先后顺序执行,在出现异步时,则发起异步请求后,接着往下执行,待异步结果返回后再接着执行。但他内部是怎样管理这些执行任务的呢? 在 js 中,任务分为宏任务(macrotask)和微任务(microtask),这两个任务分别维护一个队列,均采用先进先出的策略进行执行!同步执行的任务都在宏任务上执行。 宏任务主要有:script(整体代码)、setTimeout、setInterval、I/O、UI 交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)。 微任务主要有:Promise.then、 MutationObserver、 process.nextTick(Node.js 环境)。 具体的操作步骤如下: 从宏任务的头部取出一个任务执行; 执行过程中若遇到微任务则将其添加到微任务的队列中; 宏任务执行完毕后,微任务的队列中是否存在任务,若存在,则挨个儿出去执行,直到执行完毕; GUI 渲染; 回到步骤 1,直到宏任务执行完毕; 这 4 步构成了一个事件的循环检测机制,即我们所称的eventloop。 前端中的事件循环eventloop机制 (反复看就完事了)

zhuanghaixin commented 4 years ago

不会

console.log(1);
setTimeout(function() {
    console.log(2);
}, 0);
new Promise(function(resolve) {
    console.log(3);
    resolve(Date.now());
}).then(function() {
    console.log(4);
    setTimeout(()=>{console.log(8)},0)
});
console.log(5);
setTimeout(function() {
    new Promise(function(resolve) {
        console.log(6);
        resolve(Date.now());
    }).then(function() {
        console.log(7);
    });
}, 0);

1,3,5,4,2,6,7,8

zhuanghaixin commented 4 years ago

Promise的几种状态

image

const p1=new Promise((resolve,reject)=>{
    resolve(1)
})

const p2=new Promise((resolve,reject)=>{
    setTimeout(()=>{
        resolve(2)
    },1000)
})

const p3=new Promise((resolve,reject)=>{
    setTimeout(() => {
        reject(3)
    },1000)
})
console.log(p1)
console.log(p2)
console.log(p3)

setTimeout(()=>{
    console.log(p2)
},2000)
setTimeout(()=>{
    console.log(p3);
},2000)

p1.then(res=>{
    console.log(res)
})
p2.then(res=>{
    console.log(res)
})
// p3.then(()=>{
// },(res)=>{
//     console.log(res)
// })  //等价于 .catch
p3.catch(res=>{
    console.log(res)
})

image

zhuanghaixin commented 4 years ago

Promise在ajax中的应用

请求a之后再请求b之后再请求c

1.用回调函数的方式会出现回调地狱

// callback hell
ajax('static/a.json', res => {
    console.log(res)
    ajax('static/b.json', res => {
        console.log(res)
        ajax('static/c.json', res => {
            console.log(res)
        })
    })
})

封装的ajax函数

// Ajax的原理
function ajax(url, callback) {
    // 1、创建XMLHttpRequest对象
    var xmlhttp
    if (window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest()
    } else { // 兼容早期浏览器
        xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
    }
    // 2、发送请求
    xmlhttp.open('GET', url, true)
    xmlhttp.send()
    // 3、服务端响应
    xmlhttp.onreadystatechange = function () {
        if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
            var obj = JSON.parse(xmlhttp.responseText)
            // console.log(obj)
            callback(obj)
        }
    }
}

2.用Promise链式调用的方式

new Promise((resolve, reject) => {
    ajax('static/a.json',res=>{
        console.log(res)
        resolve()
    })
}).then(()=>{
    console.log('a成功')
    return new Promise((resolve, reject)=>{
        ajax('static/b.json',res=>{
            console.log(res)
            resolve()
        })
    })
}).then(()=>{
    console.log('b成功')
    return new Promise((resolve, reject)=>{
        ajax('static/c.json',res=>{
            console.log(res)
            resolve()
        })
    })
}).then(()=>{
    console.log('c成功')
})

3.对Promise进行封装

function getPromise(url) {
    return new Promise((resolve, reject) => {
        ajax(url, res => {
            resolve(res)
        })
    })
}

getPromise('static/a.json')
    .then(res => {
        console.log(res)
        return getPromise('static/b.json')
    })
    .then(res => {
        console.log(res)
        return getPromise('static/c.json')
        }
    )
    .then(res => {
        console.log(res)
    })

请求a之后再请求b之后再请求c(失败情况)

封装的ajax函数


// Ajax的原理
function ajax(url, successCallback, errorCallback) {
    // 1、创建XMLHttpRequest对象
    var xmlhttp
    if (window.XMLHttpRequest) {
        xmlhttp = new XMLHttpRequest()
    } else { // 兼容早期浏览器
        xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')
    }
    // 2、发送请求
    xmlhttp.open('GET', url, true)
    xmlhttp.send()
    // 3、服务端响应
    xmlhttp.onreadystatechange = function () {
        if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
            var obj = JSON.parse(xmlhttp.responseText)
            // console.log(obj)
           successCallback && successCallback(obj) //函数和有就调用,函数没有传递,就不调用
        }else  if (xmlhttp.readyState === 4 && xmlhttp.status === 404) {
            errorCallback && errorCallback(xmlhttp.responseText)        }
    }
}

第一种情况 (对第一个错误进行打印)

function getPromise(url) {
    return new Promise((resolve, reject) => {
        ajax(url, res => {
            resolve(res)
        },err=>{
            reject(err)
        })
    })
}

getPromise('static/aa.json')
    .then(res => {
        console.log(res)

    },err=>{
        console.log(err)
        // return getPromise('static/b.json')
    })
    .then(res => {
        console.log(res)
        // return getPromise('static/c.json')
        }
    )
    .then(res => {
        console.log('最后一次then')
        console.log(res)
    })

image

第二种情况 (对第一个错误进行打印,并执行)

function getPromise(url) {
    return new Promise((resolve, reject) => {
        ajax(url, res => {
            resolve(res)
        },err=>{
            reject(err)
        })
    })
}

getPromise('static/aa.json')
    .then(res => {
        console.log(res)

    },err=>{
        console.log(err)
        // return getPromise('static/b.json')
    })
    .then(res => {
        console.log(res)
        return getPromise('static/c.json')
        }
    )
    .then(res => {
        console.log('最后一次then')
        console.log(res)
    })

image

第三种情况 (都a,b,c失败情况统一处理)

function getPromise(url) {
    return new Promise((resolve, reject) => {
        ajax(url, res => {
            resolve(res)
        },err=>{
            reject(err)
        })
    })
}

getPromise('static/aa.json')
    .then(res => {
        console.log(res)

    })
    .then(res => {
        console.log(res)
        return getPromise('static/c.json')
        }
    )
    .then(res => {
        console.log(res)
    })
    .catch(err => {
        console.log('文件找不到')
        console.log(err)
    })

image

zhuanghaixin commented 4 years ago

Promise的静态方法

Promise.resolve()

let p1 = Promise.resolve('success')
// console.log(p1)
p1.then(res => {
    console.log(res)
})

Promise.reject()

``js let p2 = Promise.reject('fail') console.log(p2) p2.catch(err => { console.log(err) })

```js
function foo(flag) {
    if (flag) {
        return new Promise(resolve => {
            // 异步操作
            resolve('success')
        })
    } else {
        // return 'fail'
        return Promise.reject('fail')
    }
}

foo(false).then(res => {
    console.log(res)
}, err => {
    console.log(err)
})

Promise.all()

都成功,才会执行

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(1)
        resolve('1成功')
    }, 2000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(2)
        resolve('2成功')
    }, 1000)
})
let p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(3)
        resolve('3成功')
    }, 3000)
})

Promise.all([p1, p2, p3]).then(res => {
    console.log(res)
}, err => {
    console.log(err)
})

image

有一个失败,Promise就认为都失败,所以执行失败的回调函数

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(1)
        resolve('1成功')
    }, 2000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(2)
        // resolve('2成功')
        reject('2失败')
    }, 1000)
})
let p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(3)
        resolve('3成功')
    }, 3000)
})

Promise.all([p1, p2, p3]).then(res => {
    console.log('成功了')
    console.log(res)
}, err => {
    console.log('失败了')
    console.log(err)
})

image

Promise.all 应用 (上传多张图片)

const imgArr = ['1.jpg', '2.jpg', '3.jpg']
let promiseArr = []
imgArr.forEach(item => {
    promiseArr.push(new Promise((resolve, reject) => {
        // 图片上传的操作
        resolve()
    }))
})
Promise.all(promiseArr).then(res => {
    // 插入数据库的操作
    console.log('图片全部上传完成')
})

Promise.race()

有一个成功,就执行成功的回调函数

let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(1)
        resolve('1成功')
    }, 1000)
})
let p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(2)
        // resolve('2成功')
        reject('2失败')
    }, 2000)
})
let p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log(3)
        resolve('3成功')
    }, 3000)
})

Promise.race([p1, p2, p3]).then(res => {
    console.log(res)
}, err => {
    console.log(err)
})

image

Promise.race的应用(获取图片超时)

function getImg() {
    return new Promise((resolve, reject) => {
        let img = new Image()
        img.onload = function () {
            resolve(img)
        }
        // img.src = 'http://www.xxx.com/xx.jpg'
        img.src = 'https://www.imooc.com/static/img/index/logo.png'
    })
}

function timeout() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('图片请求超时')
        }, 2000)
    })
}

Promise.race([getImg(), timeout()]).then(res => {
    console.log(res)
}).catch(err => {
    console.log(err)
})
zhuanghaixin commented 3 years ago

这道题输出什么?

image

zhuanghaixin commented 3 years ago

image