文章目录
一、什么是Promise
Promise是es6为了方便异步编程的新的解决方案,从语法上来看它是一个构造函数,用于封装异步任务,并且对其结果进行处理
//异步编程
//fs文件操作
require('fs').readFile('./index.html',(err,data)=>{})
//数据库操作
//AJAX
$.get('/server',(data)=>{})
//定时器
setTimeout(()=>{},2000)
二、Promise优缺点
1.promise优点
- 指定回调函数的方式更加灵活
- 支持链式调用,可以解决回调地狱问题
- 更好的进行错误捕捉
2.promise缺点
- 无法取消promise,一代新建就会立即执行,无法中途取消
- 如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部
- 当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)
三、Promise的创建
promise构造函数包含一个参数和一个带有resolve和reject两个参数的回调。在回调中执行一些操作,如果一切正常则调用resolve,否则调用reject。
setTimeout模拟异步操作:
var myFirstPromise = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function(successMessage) {
document.write("Yay! " + successMessage);
});
四、Promise实践
1、promise读取文件
let promise= new Promise((resolve,reject)=>{
const fs = require('fs')
fs.readFile('./data.txt',(err,data)=>{
if (err) reject(err);
resolve(data)
})
})
//调用then
promise.then(value => {
console.log(value.toString())
},reason => {
console.log(reason)
})
2、promise封装AJAX
<body>
<button>点击发送请求</button>
</body>
<script>
var promise = new Promise(function(resolve, reject) {
const btn = document.querySelector('button')
btn.addEventListener('click',function(){
//1.创建对象
const xhr = new XMLHttpRequest()
//2.初始化
xhr.open('GET','https://api.apiopen.top/getJoke')
//3.发送
xhr.send()
//4.处理响应结果
xhr.onreadystatechange = function(){
if (xhr.readyState ===4 ){
if (xhr.status>=200&&xhr.response){
resolve(xhr.response)
}else {}
reject(xhr.status)
}
}
})
});
promise.then(value => {
console.log(value)
},reason => {
console.log(reason)
});
</script>
3.Promise封装函数
function mineReadFile(Path){
return new Promise((resolve,reject)=>{
//读取文件
require('fs').readFile(Path,(err,data)=>{
if (err) reject(err);
resolve(data)
})
})
}
mineReadFile('./data.txt').then(value => {
console.log(value.toString())
},reason => {
console.log(reason)
})
4.promisify方法
传入一个错误优先的回调风格的函数(以(err,value)=>…会带哦作为最后一个参数),并返回一个Promise版本
//引入util模块
const util = require('util');
//引入fs模块
const fs = require('fs')
let mineReadFile = util.promisify(fs.readFile);
mineReadFile('./data.txt').then(value => {
console.log(value.toString())
})
五、Promise状态
描述:实例对象中的一个属性[PromiseState]
三种状态:
- pending(进行中)
- resolved/fulfilled(已成功)
- rejected(已失败)
状态改变:
- pending变为resolved
- pending变为reje。cted
PS:只有这两种变化,且一个promise对象只能改变一次
六、Promise对象的值
描述:实例对象的另一个属性[PromiseResult]
保存着异步任务【成功/失败】的结果
只有resolve和reject可以对该值进行修改
七、Promise的API
1. Promise构造函数:
Promise(excutor){}
- executor 函数:执行器 (resolve,reject)=>{}
- resolve函数:内部定义成功时调用的函数 value=>{}
- reject函数:内部定义失败时调用的函数reason=>{}
PS:executor会在Promise内部立即同步调用,异步操作在执行器中执行
2.Promise.prototypr.then方法:
(onRsolved.onRejected)=>{}
- onResolved函数:成功的回调函数(value)=>{}
- onRejected函数:失败的回调函数(reason)={}
PS:指定用于得到成功value的成功回调和用于得到失败reason的失败会带哦返回一个新的promise对象
3.Promise.prototype.catch方法:
(onRejected)=>{}
- onRejected函数:失败的回调函数(reason)=>{}
PS:then()的语法糖,相当于:then(undefined,onRejected)
4.Promise.resolve方法:
(value)=>{}
- value:成功的数据或者promise对象
PS:返回一个成功/失败的promise对象
5.Promise.reject方法:
(reason)=>{}
- reason:失败的原因
PS:返回一个失败的promise对象
6. Promise.all方法:
(promise)=>{}
- promises:包含n个promise的数组
PS:返回一个新的promise,只有所有的promise都成功才算成功
let p1 = new Promise((resolce,reject)=>{
resolce('OK')
})
// let p2 = Promise.resolve('Sucess')
let p2 = Promise.reject('Error')
let p3 = Promise.resolve('Oh Yeah')
const res= Promise.all([p1,p2,p3])
console.log(res)
7.Promise.race方法:
(promise)=>{}
- promises:包含n个promise的数组
PS:返回一个新的promise,第一个完成的promise的结果状态就是最终的结果状态
let p1 = new Promise((resolce,reject)=>{
resolce('OK')
})
// let p2 = Promise.resolve('Sucess')
let p2 = Promise.reject('Error')
let p3 = Promise.resolve('Oh Yeah')
const res= Promise.race([p1,p2,p3])
console.log(res)
八、如何改变promise的几个关键问题
1.如何改变promise的状态
- resolve(value): 如果当前时pending就会变成resolved
- reject(reason): 如果当前时pending就会变成rejected
- 抛出异常:如果档期那时pending就会变成rejected
<script>
let p1 = new Promise((resolve,reject)=>{
// 1.resolve函数
// resolce('OK') // pending =>fulfilled(resolved)
// 2.reject函数
// reject('error') //pending => rejected
// 3. 抛出错误
throw 'erro';
})
console.log(p1)
</script>
2.一个promise指定多个成功/失败回调函数时,都会调用吗?
当promise改变未对应状态时都会调用
let p1 = new Promise((resolve,reject)=>{
resolve('OK')
})
p1.then(value => {
console.log(value)
})
p1.then(value => {
alert(value)
})
3.改变promise状态和指定回调函数谁先谁后?
- 都有可能,正常情况下时限指定回调在改变状态,但也可以先改变状态再指定回调
- 如何先改变状态再指定回调?
- 再执行器中直接调用resolve()/reject()
- 延迟更长时间调用then()
- 什么时候才能得到数据?
- 如果先指定的回调,那么当状态发生改变时,回调函数就会调用,得到数据。
- 如果先改变的状态,那指定回调时,回调函数就会调用,得到数据。
4.promise.then()返回的新promise的结果状态由什么决定?
- 简单表达:由then()指定的回调函数执行的结果决定。
- 详细表达:
- 如果抛出异常,新promise变为rejected,reason为抛出的异常。
- 如果返回的是非promise的任意值,新promise变为resolved,value为返回的值。
- 如果返回的是一个新promise,此promise的结果就会成为新promise的结果。
5.promise如何串联多个操作任务?
- promise的then()返回一个新的promise,可以开发成then()的链式调用。
- 通过then()的链式调用串联多个同步/异步任务。
//后一个回调返回的时前一个回调函数的结果
let p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},1000)
})
p1.then(value => {
return new Promise((resolve,reject)=>{
resolve('success')
})
}).then(value => {
console.log(value)// 打印success
}).then(value => {
console.log(value)//undefined 上一个函数没有指定返回值
})
6.promise异常穿透?
- 当使用promise的then()链式调用时,可以再最后指定失败的回调。
- 前面任何操作除了异常,都会传到最后失败的回调中处理。
let p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
// reject('err')
resolve('ok')
},1000)
})
p1.then(value => {
// console.log(11)
throw '失败了'
}).then(value => {
console.log(22)
}).then(value => {
console.log(33)
}).catch(reason => {
console.log(reason)
})
7.中断promise链?
- 当使用promise的then()链式调用时,在中间中断,不在调用后面的回调函数。
- 方法:在回调函数中返回一个pendding状态的promise对象
let p1 = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('ok')
},1000)
})
p1.then(value => {
console.log(11)
// 有且只有一个方式
return new Promise(()=>{})
}).then(value => {
console.log(22)
}).then(value => {
console.log(33)
}).catch(reason => {
console.log(reason)
})
九、手写Promise
class Promise {
//构造方法
constructor(executor) {
//添加属性
this.PromiseState = 'pending'
this.PromiseResult = null
// 声明属性
this.callbacks = []
// 保存实例对象this的值
const self = this;
// resolve
function resolve(data) {
//判断状态
if (self.PromiseState !== 'pending') return
// 1.修改状态 promiseState
self.PromiseState = 'fulfilled' //resolved
// 2.设置对象结果的值 promiseResult
self.PromiseResult = data
// 调用成功的回调函数
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(data)
})
})
}
// reject
function reject(data) {
if (self.PromiseState !== 'pending') return
self.PromiseState = 'rejected' //resolved
self.PromiseResult = data
setTimeout(() => {
self.callbacks.forEach(item => {
item.onRejected(data)
})
})
}
try {
//同步调用执行器函数
executor(resolve, reject);
} catch (e) {
//修改promise对象状态为【失败】
reject(e)
}
}
// then方法
then(onResolved, onReject) {
const self = this
// 判断回调函数参数
if (typeof onReject !== 'function') {
onReject = reason => {
throw reason
}
}
if (typeof onResolved !== 'function') {
onResolved = value => value;
}
return new Promise((resolve, reject) => {
//封装函数callback
function callback(type) {
try {
//获取回调函数的执行结果
let result = type(self.PromiseResult)
if (result instanceof Promise) {
//如果时Promise类型的对象
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
// 结果的对象状态为成功
resolve(result)
}
} catch (e) {
reject(e)
}
}
//调用回调函数 PromiseState
if (this.PromiseState === 'fulfilled') {
//将then方法改为异步
setTimeout(() => {
callback(onResolved)
})
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onReject)
})
}
// 判断pending状态
if (this.PromiseState === 'pending') {
// 保存回调函数
this.callbacks.push({
onResolved: function() {
callback(onResolved)
},
onRejected: function() {
callback(onReject)
},
})
}
})
}
// catch方法
catch(onReject) {
return this.then(undefined, onReject)
}
// resolve方法
static resolve(value) {
//添加Promise对象
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(value)
}
})
}
// 添加reject方法
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
// 添加all方法
static all(promise) {
// 返回结果时Promise对象
return new Promise((resolve, reject) => {
//定义一个计数器
let count = 0
let array = []
// 遍历
for (let i = 0; i < promise.length; i++) {
promise[i].then(v => {
//当所有对象都是成功的
count++
//将成功的对象存入到数组中
array[i] = v
if (count === promise.length) {
resolve(array);
}
}, r => {
reject(r)
})
}
})
}
// 添加race方法
static race(promise) {
return new promise((resolve, reject) => {
for (let i = 0; i < promise.length; i++) {
promise[i].then(v => {
resolve(v)
}, r => {
reject(r)
})
}
})
}
}
十、async和await
1.async函数
- 函数的返回值为Promise对象
- promise对象的结果由async函数执行的返回值决定
async function main(){
//1.如果返回值是一个非Promise的数据
// return 321
//2.如果返回 值是一个promise对象
// return new Promise((resolve,reject)=>{
// reject('ERROR')
// })
//3.抛出异常
throw 'NO'
}
let result = main()
console.log(result)
2. await表达式
- await右侧的表达式一般为Promise对象,但也可以是其他的值
- 如果表达式时promise对象,await返回的时prmise成功的值
- 如果表达式是其他值,直接将此值转会为await的返回值
async function main(){
let p = new Promise((resolve,reject)=>{
// resolve('ok')
reject('error')
})
//1.右侧为promise
// let res = await p; //ok
//2.右侧为其他类型的数据
// let res2 =await 20; //20
//3.promise是失败的状态
try{
let res3 = await p //error
}catch (e){
console.log(e)
}
}
main()
3.aync和await结合
案例1
可以看到和回调函数的方式比起来,这种方式非常
const fs = require('fs')
const util = require('util')
const mineReadFile = util.promisify(fs.readFile)
// 回调函数
// fs.readFile('../resource/data.txt',(err,data)=>{
// if (err) throw err
// fs.readFile('../resource/data1.txt',(err,data1)=>{
// if (err) throw err
// fs.readFile('../resource/data2.txt',(err,data2)=>{
// if (err) throw err
// console.log((data+data1+data2).toString())
// })
// })
// })
// async 与await
async function main() {
try {
//读取第一个文件的内容
let data = await mineReadFile('../resource/data.txt')
let data1 = await mineReadFile('../resource/data1.txt')
let data2 = await mineReadFile('../resource/data2.txt')
console.log((data + data1 + data2).toString())
}catch (e){
console.log(e)
}
}
main()
案例2
封装AJAX请求
function sendAJAX(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest()
xhr.responseType = 'json'
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)
}
}
}
})
}
// sendAJAX('https://api.apiopen.top/getJoke')
// .then(value => {
// console.log(value)
// }, reason => {
// console.warn(reason)
// })
let btn = document.querySelector('button')
btn.addEventListener('click', async function() {
//获取段子
let res = await sendAJAX('https://api.apiopen.top/getJoke')
console.log(res)
})