1.Promise是什么
Promise是JS处理异步编程的一种解决方案。从语法上看,Promise是一个构造函数;从功能上看,可以用来封装一个异步操作并且可以获取其成功/失败的结果值。
常见的异步编程: fs文件操作、Ajax、定时任务
- fs文件操作
(1) 回调函数形式:
// 引入fs模块
const fs = require('fs')
fs.readFile('./context.txt', (err, data) => {
// 如果出错,抛出异常
if(err) throw err
// 否则输出文件内容
console.log(data.toString());
})
(2)promise实现:
// 引入fs模块
const fs = require('fs')
const p = new Promise((resolve, reject) => {
fs.readFile('./context.txt', (err, data) => {
if(err) reject(err)
resolve(data)
})
})
p.then(res => {
console.log(res.toString());
}, err => {
console.log(err);
})
(3)封装fs模块:
function myReadFile(path) {
// 读取文件
return new Promise((resolve, reject) => {
// 读取文件
require('fs').readFile(path, (err, data) => {
// 判断
if(err) reject(err)
resolve(data)
})
})
}
myReadFile('./context.txt').then(res => {
console.log(res.toString());
}, err => {
console.log(err);
})
- Ajax
(1) promie实现:
<body>
<div>
<p>Promis封装Ajax</p>
<button class="btn">发送Ajax请求</button>
</div>
<script>
// 获取元素
const btn = document.querySelector('.btn')
// 绑定事件
btn.addEventListener('click', function(){
// 创建promise
const p = new Promise((resolve, reject) => {
// 创建xhr对象
const xhr = new XMLHttpRequest()
// 初始化连接
xhr.open('GET', 'http://localhost:3000/person')
// 发送请求
xhr.send()
// 处理响应结果
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
// 判断响应状态码
if(xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response)
} else {
reject(xhr.status)
}
}
}
})
// 调用then
p.then(res => {
console.log(res);
}, err => {
console.log(err);
})
})
</script>
</body>
(2)封装ajax:
function sendAjax(path) {
// 创建promise
return new Promise((resolve, reject) => {
// 创建xhr对象
const xhr = new XMLHttpRequest()
// 初始化
xhr.open('GET', path)
// 响应体类型
xhr.responseType = 'json';
// 发送请求
xhr.send()
// 处理响应结果
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 判断响应状态码
if (xhr.status >= 200 && xhr.status < 300) resolve(xhr.response)
reject(xhr.status)
}
}
})
}
sendAjax('http://localhost:3000/person').then(res => {
console.log(res);
}, err => {
console.log(err);
})
- 定时任务
2.为什么要使用Promise
- 支持链式调用,可以解决回调地狱问题
什么是回调地狱?
回调函数的嵌套使用,外部回调函数异步执行的结果是嵌套的回调执行的条件。回调地狱的缺点?
不便于阅读;不便于异常处理解决方案?
promise的链式调用终极解决方案?
aync/await
- 指定回调函数的方式更灵活
- 旧的方式: 必须在启动异步任务前指定。
- promise:启动异步任务 => 返回promise对象 => 给promise对象绑定回调函数(甚至可以在异步任务结束后指定多个)
3.Promise的执行流程
4.Promise的使用
4.1 Promise构造函数:Promise(excutaor) {}
- excutor函数:执行器,(resolve, reject) => {},excutor会在Promise内部立即同步执行,异步操作在执行器中执行;
- resolve函数:内部定义成功时调用的函数data => {};
- reject函数: 内部定义失败时调用的函数err => {}。
4.2 Promise.prototype.then方法:(onResolved, onrejected) => {}
- onResolved函数:成功的回到函数data => {};
- onRejected函数:失败的回调函数err => {}。
- 指定用于得到成功data的成功回调和用于的得到失败err的失败回调都是返回一个新的promise对象。
let p = new Promise((resolve, reject) => {
resolve('suceess')
// reject('error')
})
console.log('p-suceess', p); // Promise {<fulfilled>: 'suceess'}
// console.log('p-error', p); // Promise {<rejected>: 'error'}
p.then(res => {
console.log('onResolve', res);
}, err => {
console.log('onReject', err);
})
4.3 Promise.prototype.catch方法: (onRejected) = {}
- onRejected函数:失败的回调函数err => {};
- then()的语法糖,相当于:then(undefined, onReject)
let p = new Promise((resolve, reject) => {
reject('error')
})
console.log('p', p) // Promise {<rejected>: 'error'}
p.catch(err => {
console.log('catch', err);
})
// 相当于:
// p.then(undefined, err => {
// console.log(err);
// })
4.4 Promise.resolve方法: (data) => {}
- data:成功的数据或promise对象;
- 结果返回一个成功/失败的promise对象。
// 传入的值为非promise对象,则返回的结果为resolve的promise对象
let p1 = Promise.resolve(111)
console.log('p1', p1); // Promise {<fulfilled>: 111}
// 传入的值为promise对象,由传入的参数结果决定resolve的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
resolve(222)
// reject(333)
}))
console.log('p2-resolv', p2); // Promise {<fulfilled>: 222}
// console.log('p2-reject', p2); // Promise {<rejected>: 333}
p2.catch(err => {
console.log(err);
})
4.5 Promise.reject方法: (err) => {}
- err: 失败的原因;
- 返回一个失败的promise对象。
let p = Promise.reject('error')
console.log('p', p); // Promise {<rejected>: 'error'}
p.catch(err => {
console.log(err);
})
let p2 = Promise.reject(new Promise((reslove, reject) => {
reslove(111)
}))
console.log('p2', p2); // Promise {<rejected>: Promise}
p2.catch(err => {
console.log(err);
})
4.6 Promise.all方法:(promises) => {}
- promises: 包含n个promise的数组;
- 返回一个新的promise,只有所有的promise都成功才成功,否则只要有一个失败了就直接失败。
let p1 = Promise.resolve(111)
let p2 = Promise.resolve(222)
let p3 = Promise.resolve(333)
let p4 = Promise.reject(444)
const result1 = Promise.all([p1, p2, p3])
console.log(result1);
// result1的打印结果:
// Promise {<pending>}
// [[Prototype]]: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: Array(3)
// 0: 111
// 1: 222
// 2: 333
// length: 3
// [[Prototype]]: Array(0)
const result2 = Promise.all([p1, p2, p4])
console.log(result2);
// result2的打印结果:
// Promise {<pending>}
// [[Prototype]]: Promise
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: 444
result2.catch(err => { })
4.7 Promise.race方法: (promises) => {}
- promises:包含n个promise的数组;
- 返回一个新的promise,第一个完成的promise的结果装填就是最终结果状态。
let p1 = new Promise((resolve, reject) => {
resolve(111)
})
let p2 = Promise.resolve(222)
let p3 = Promise.reject(333)
let result = Promise.race([p3, p2, p1])
result.catch(err => {})
console.log(result);
// result的打印结果:
// Promise {<pending>}
// [[Prototype]]: Promise
// [[PromiseState]]: "rejected"
// [[PromiseResult]]: 333
5.Promise的关键问题
5.1 如何改变promise的状态?
- resolve(data): 当前状态由peding -> fullfilled
- reject(err): 当前状态由pending -> rejected
- throw抛出异常: 当期状态由peddign -> rejected
let p = new Promise((resolve, reject) => {
// 1.使用resolve: 状态由pending => fullfilled
// resolve('success')
// 2.使用reject: 状态由pending => rejected
// reject('error')
// 3.抛出异常: 状态由pending => rejected
throw '有异常了'
})
p.catch(err => {
console.log(err);
})
// console.log('resolve', p); // Promise {<fulfilled>: 'success'}
// console.log('reject', p); // Promise {<rejected>: 'error'}
console.log('throw', p); // Promise {<rejected>: '有异常了'}
5.2 一个promise指定多个成功/失败回调函数,都会调用吗?
当promise改变为对应状态时都会调用
// promise的状态变为对应的状态都会调用
let p = new Promise((resolve, reject) => {
resolve('ok')
})
// 指定回调1
p.then(res => {
console.log('回调1', res); // ok
})
// 回调2
p.then(res => {
console.log('回调2', res); // ok
})
let p2 = new Promise((resolve, reject) => {
reject('error')
})
p2.catch(err => {
console.log(err); // error
})
p2.catch(err => {
console.log(err); // error
})
5.3 改变promise状态和指定回调函数谁先谁后?
- 都有可能,正常情况先指定回调再改变状态,但也可以先改变状态再指定回调
- 如何先改变状态再指定回调?
在执行器中直接调用resolve()/reject();延迟更长时间调用then() - 什么时候才能得到数据?
如果是先指定回调,当状态发生改变,回调函数就会调用,得到数据;如果是先改变状态,指定回调时,回调函数就会调用,得到数据。
5.4 promise.then()返回的promise的状态由什么决定?
- 简单表达式由then()指定的回调函数指向的结果决定
- 详细表达:
(1)如果返回另一个新的promise,此promise的结果就是新的promise的结果;
(2)如果返回一个非promise的值,新的promise变为resolved,data就是返回的值;
(3)如果抛出异常,新的promise变为rejected,err为抛出的异常
5.5 promise如何串连多个操作任务?
promise的then()返回新的promise,所以可以通过then()的链式调用串连多个同步或异步任务
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
})
})
// 通过then()方法来串联多个任务
p.then(res => {
console.log(res);
}).then(res => {
console.log(222);
}).then(res => {
console.log(333);
})
5.6 promise异常穿透
当使用promise的then链式调用时,可以在最后指定失败的回调,前面任何操作出了异常,都会传到最后的回调中处理。
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
// reject('error')
}, 100)
})
p.then(res => {
console.log(111);
}).then(res => {
throw '出错了'
console.log(222);
}).then(res => {
console.log(33);
}).catch(err => {
// 使用catch来指定失败的回调
console.log(err);
})
5. 7 如何中断promise链?
在回调函数中返回一个pending状态的promise对象。
let p = new Promise((resolve, reject) => {
resolve('success')
})
p.then(res => {
console.log(111); // 111 只有治理打印了
// 使用状态为pending的promise来中断promise链
return new Promise((resolve, reject) => {})
}).then(res => {
console.log(222);
}).then(res => {
console.log(333);
}).catch(err => {
console.log(err);
})
6.async和await
- async函数:该函数返回一个promise对象,promise对象的结果由async函数执行的返回值决定。
async function main() {
// 1.函数执行返回值是非promise对象
// return 11
// 2.返回值是一个promise对象
// return new Promise((resolve, reject) => {
// // reject('error')
// resolve('ok')
// })
// 抛出异常
throw 'Error'
}
const result = main()
// console.log('非promise', result); // 非promise Promise {<fulfilled>: 11}
result.catch(err => { })
// console.log('promise-reject', result);
// // promise - reject
// // Promise {<pending>}
// // [[Prototype]]: Promise
// // [[PromiseState]]: "rejected"
// // [[PromiseResult]]: "error"
// console.log('promise-resolve', result);
// // promise-resolve
// // Promise {<pending>}
// // [[Prototype]]: Promise
// // [[PromiseState]]: "fulfilled"
// // [[PromiseResult]]: "ok"
console.log('throw', result); // throw Promise {<rejected>: 'Error'}
- await表达式: await右侧的表达式一般为promise对象,也可以是其他值。如果是promise对象,await返回promise成功的值;如果是其他值,直接将其作为await的返回值。
async function main() {
let p = new Promise((resolve, reject) => {
// resolve('success')
reject('error')
})
// 1.await右侧为其他类型的数据, 直接将其作为返回值
// let res = await 22
// console.log(res); // 22
// 2.await右侧为promise的情况
// 2.1成功状态
// let res2 = await p
// console.log(res2); // sucess
// 2.2 失败状态
try{
let res3 = await p
} catch(e) {
console.log(e);
}
}
main()