Promise 是什么?
- JS 中进行异步编程的新解决方案
- promise 对象用来封装一个异步操作并可以获取其成功/失败的结果值
Promise 的状态(PromiseState)及改变
状态:
实例对象
中的一个属性
- pending 未决定的
- resolved / fullfilled 成功
- rejected 失败
状态改变:
- pending 变为 resolved
- pending 变为 rejected
注:
只有这 2 种, 且一个 promise 对象只能改变一次
无论变为成功还是失败, 都会有一个结果数据
成功的结果数据一般称为 value, 失败的结果数据一般称为 reason
Promise 对象的值(PromiseResult)
实例对象
中的另一个属性
保存着异步任务『成功/失败』的结果
- resolve
- reject
Promise 工作流程
API
1. Promise 构造函数: Promise (excutor) {}
(1) executor 函数: 执行器 (resolve, reject) => {}
(2) resolve 函数: 内部定义成功时我们调用的函数 value => {}
(3) reject 函数: 内部定义失败时我们调用的函数 reason => {}
说明: executor 会在 Promise 内部立即同步调用
,异步操作在执行器中执行
2. Promise.prototype.then 方法: (onResolved, onRejected) => {}
(1) onResolved 函数: 成功的回调函数 (value) => {}
(2) onRejected 函数: 失败的回调函数 (reason) => {}
说明: 指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调
返回一个新的 promise 对象
3. Promise.prototype.catch 方法: (onRejected) => {}
(1) onRejected 函数: 失败的回调函数 (reason) => {}
说明: then()的语法糖, 相当于: then(undefined, onRejected)
4. Promise.resolve 方法: (value) => {}
(1) value: 成功的数据或 promise 对象
- 如果传入的参数为 非Promise类型的对象, 则返回的结果为成功promise对象
- 如果传入的参数为 Promise 对象, 则参数的结果决定了 resolve 的结果
说明: 返回一个成功/失败的 promise 对象
5. Promise.reject 方法: (reason) => {}
(1) reason: 失败的原因
说明: 返回一个失败的 promise 对象
6. Promise.all 方法: (promises) => {}
(1) promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise, 只有所有的 promise 都成功才成功, 只要有一个失败了就直接失败
7. Promise.race 方法: (promises) => {}
(1) promises: 包含 n 个 promise 的数组
说明: 返回一个新的 promise, 第一个完成的 promise 的结果状态就是最终的结果状态
关键问题:
如何改变 promise 的状态?
- resolve(value): 当前是 pending 就会变为 resolved
- reject(reason): 当前是 pending 就会变为 rejected
- 抛出异常: 如果当前是 pending 就会变为 rejected
let p = new Promise((resolve, reject) => {
// 1. resolve 函数
resolve('ok'); // pending => fulfilled (resolved)
// 2. reject 函数
reject("error");// pending => rejected
// 3. 抛出错误
throw '出问题了';
});
一个 promise 指定多个成功/失败回调函数, 都会调用吗?
当 promise 改变为对应状态时都会调用
let p = new Promise((resolve, reject) => {
resolve('OK');
});
///指定回调 - 1
p.then(value => {
console.log(value);
});
//指定回调 - 2
p.then(value => {
alert(value);
});
//以上两个回调函数都会调用
改变 promise 状态和指定回调函数谁先谁后?
- 都有可能, 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调
- 如何先改状态再指定回调?
① 在执行器中直接调用 resolve()/reject()
② 延迟更长时间才调用 then() - 什么时候才能得到数据?
① 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
② 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
then()返回的新 promise 的结果状态由什么决定?
- 简单表达: 由 then()指定的回调函数执行的结果决定
- 详细表达:
① 如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常
② 如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值
③ 如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果
promise 如何串连多个操作任务?
(1) promise 的 then()返回一个新的 promise, 可以开成 then()的链式调用
(2) 通过 then 的链式调用串连多个同步/异步任务
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
});
p.then(value => {
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => {
console.log(value);
}).then(value => {
console.log(value);
})
promise 异常传透?
(1) 当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调,
(2) 前面任何操作出了异常, 都会传到最后失败的回调中处理
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
// reject('Err');
}, 1000);
});
p.then(value => {
// console.log(111);
throw '失败啦!';
}).then(value => {
console.log(222);
}).then(value => {
console.log(333);
}).catch(reason => {
console.warn(reason);
});
中断 promise 链?
(1) 当使用 promise 的 then 链式调用时, 在中间中断, 不再调用后面的回调函数
(2)有且只有一种办法: 在回调函数中返回一个 pendding 状态的 promise 对象
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
});
p.then(value => {
console.log(111);
//有且只有一个方式
return new Promise(() => { });
}).then(value => {
console.log(222);
}).then(value => {
console.log(333);
}).catch(reason => {
console.warn(reason);
});
Promise 封装 Ajax的GET请求
/**
* 封装一个函数 sendAJAX 发送 GET请求
* 参数 URL
* 返回结果 Promise 对象
*/
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.log(reason);
}
)
Promise封装读取文件操作
1. 手动封装函数
/**
* 封装一个函数 mineReadFile 读取文件内容
* 参数: path 文件路径
* 返回: promise 对象
*/
function read(path) {
//Promise参数,第一个为resolve,第二个为reject
return new Promise((resolve, reject) => {
require('fs').readFile(path, (err, data) => {
if (err) reject(err)
resolve(data)
})
})
}
read('./content.txt')
.then(value => {
console.log(value.toString());
},
reason => {
console.log(reason);
}
)
2. Promisify
//引入util模块
const util = require('util')
//引入fs模块
const fs = require('fs')
//返回一个新的函数,promisify返回一个promise对象,不用手动封装
const read = util.promisify(fs.readFile)
read('./content.txt')
.then(value => {
console.log(value.toString());
},
reason => {
console.log(reason);
}
)
自定义封装Promise
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script crossorigin="anonymous" src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.1/axios.js"></script>
<script src="./promise.js"></script>
</head>
<body>
<div class="container">
<h2 class="page-header">Promise封装AJAX请求</h2>
<button class="btn btn-primary">点击发送</button>
</div>
<script>
// let p = new Promise((resolve, reject) => {
// //假设得异步情况
// setTimeout(() => {
// // reject('error')
// resolve('ok')
// }, 1000)
// })
// 1.then回调函数
// const res = p.then(value => {
// console.log(value)
// // return 'hello'
// // return new Promise((resolve, reject) => {
// // resolve('happy new year')
// // // reject('Error')
// // })
// // throw 'FAIL'
// }, reason => {
// console.warn(reason);
// })
// console.log(res)
// then多个回调
// p.then(value => {
// alert(value);
// }, reason => {
// alert(reason);
// })
//2.catch回调--指定失败的回调函数
// let res = p.catch(reason => {
// console.log(reason);
// })
// console.log(res);
// p.then(value => {
// console.log(111);
// throw 'ERROR'
// }).then(value => {
// console.log(222);
// }).then(value => {
// console.log(333);
// }).catch(reason => {
// console.log(reason);
// })
//值传递,onResolved无
// p.then().then(value => {
// console.log(222);
// }).then(value => {
// console.log(333);
// }).catch(reason => {
// console.log(reason);
// })
// 3.Resolve方法 传入的值是成功的,状态是成功的;传入的是失败的,也是失败的
// const p = Promise.resolve('OK')
// const p2 = Promise.resolve(new Promise((resolve, reject) => {
// resolve('success')
// }))
// const p3 = Promise.resolve(Promise.resolve('oh yeah'))
// console.log(p3);
// 4.reject方法
// const p = Promise.reject('Error')
// const p2 = Promise.reject(new Promise((resolve, reject) => {
// resolve('OK')
// }))
// console.log(p)
// console.log(p2)
// 5.all方法
// let p1 = new Promise((resolve, reject) => {
// resolve('OK')
// })
// let p2 = Promise.resolve('Success')
// let p3 = Promise.resolve('Oh yeah')
// let result = Promise.all([p1, p2, p3])
// console.log(result);
// 6. race方法
// let p1 = new Promise((resolve, reject) => {
// setTimeout(() => {
// resolve('OK')
// })
// })
// let p2 = Promise.reject('Success')
// let p3 = Promise.resolve('Oh yeah')
// let result = Promise.race([p1, p2, p3])
// console.log(result);
// 7.then方法 回调函数,异步执行
// let p1 = new Promise((resolve, reject) => {
// resolve('OK')
// console.log(111);
// })
// p1.then(value => {
// console.log(222);
// })
// console.log(333);
// class测试
// let p1 = new Promise((resolve, reject) => {
// setTimeout(() => {
// // resolve('OK')
// // reject('Error')
// })
// // resolve('OK')
// // reject('Error')
// // throw "Error"
// })
// p1.then(value => {
// console.log(value);
// }, reason => {
// console.log(reason);
// })
// console.log(p1);
console.log(Promise.resolve('OK'));
</script>
</body>
</html>
Promise.js
class Promise {
constructor(executor) {
//添加属性
this.PromiseState = 'pending'
this.PromiseResult = null
const self = this
// 单个
// this.callback= {}
this.callbacks = []
// resolve函数
function resolve(data) {
//这里的this指向的是window
// console.log(this)
//设置对象的状态只能改变一次
if (self.PromiseState !== 'pending') return
//1.修改对象状态
self.PromiseState = 'fulfilled'
//2.设置对象结果值
self.PromiseResult = data
//执行回调
// 单个
// if (self.callback.onResolved) {
// self.callback.onResolved(data)
// }
// 多个
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(data)
})
})
}
//reject函数
function reject(data) {
//设置对象的状态只能改变一次
if (self.PromiseState !== 'pending') return
//1.修改对象状态
self.PromiseState = 'rejected'
//2.设置对象结果值
self.PromiseResult = data
// 多个
setTimeout(() => {
self.callbacks.forEach(item => {
item.onRejected(data)
})
})
}
try {
//同步调用执行器函数
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
then(onResolved, onRejected) {
const self = this
//判断回调函数参数
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason
}
}
if (typeof onResolved !== 'function') {
onResolved = value => value
// value => { return value }
}
//then方法返回一个Promise对象
return new Promise((resolve, reject) => {
function callback(type) {
try {
//获取回调函数得执行结果
let result = type(self.PromiseResult)
if (result instanceof Promise) {
//result是Promise对象
//成功与失败的回调函数
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
//不是Promise对象
//将then返回结果的Promise状态置为成功,值为回调函数的执行结果
resolve(result)
}
} catch (e) {
reject(e)
}
}
// 调用回调函数
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onResolved)
})
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected)
})
}
//判断pending状态
if (this.PromiseState === 'pending') {
this.callbacks.push({
onRejected: function () {
callback(onRejected)
},
onResolved: function () {
callback(onResolved)
}
})
}
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
static resolve(value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(value)
}
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
static all(promises) {
return new Promise((resolve, reject) => {
let cnt = 0
let arr = []
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
cnt++
// arr[i] = v
if (cnt === promises.length) {
resolve(arr)
}
}, r => {
reject(v)
})
}
})
}
static race(promises) {
return new Promise((resolve, reject) => {
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
resolve(v)
}, r => {
reject(r)
})
}
})
}
}
async函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
// async函数返回值为一个Promise对象
// Promise对象的结果由async函数执行的返回值决定
async function main() {
// 1.可以返回一个非Promise类型的数据
// return 512
// 2. 可以返回一个Promise对象
// return new Promise((resolve, reject) => {
// // resolve('OK')
// reject('Error')
// })
// 3.抛出异常
throw "Oh,no"
}
let result = main()
console.log(result);
</script>
</body>
</html>
await表达式:只能放在async中
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
async function main() {
let p = new Promise((resolve, reject) => {
resolve('OK')
// reject('Error')
})
// 1.右侧为Promise对象
// 当Promise是成功的状态,返回成功的值
// 当Promise是失败的状态,抛出错误
try {
let result = await p
console.log(result);
} catch (e) {
console.log(e);
}
// 2.右侧为其他类型的数据,返回该数据
// let result = await 20
// console.log(result);
}
main()
</script>
</body>
</html>
async与await结合
发送AJAX请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>async与await结合发送AJAX</title>
</head>
<body>
<button id="btn">点击获取段子</button>
<script>
//axios
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);
}
}
}
});
}
//段子接口地址 https://api.apiopen.top/getJoke
let btn = document.querySelector('#btn');
btn.addEventListener('click',async function(){
//获取段子信息
let duanzi = await sendAJAX('https://api.apiopen.top/getJoke');
console.log(duanzi);
});
</script>
</body>
</html>