这是我参与更文挑战的第6天,活动详情查看: 更文挑战
Promise 是一门新的技术,是JS中进行异步编程的心解决方案,从语法上说,Promise是一个构造函数,从功能上来说,Promise对象用了封装一个异步操作并可以获取到其成功失败的结果值。
1, 常见的异步编程
1, fs 文件操作
require('fs').readFile('./index.html', (err, data) => {})
2,数据库操作
3,AJAX
$.get('./server', (data) => {})
4,定时器
setTimeout(() => {}, 200)
2, Promise的特点
支持链式调用,可以解决回调地狱问题
(1) 文件的读取
// 以前读取文件的写法
const fs = require('fs')
fs.readFile('./package/content.txt',(err, data) => {
// 如果错误,抛出错误
if(err) throw err;
//否则输出文件内容
console.log(data.toString())
})
// 现在promise形式
let p = new Promise((resolve, reject) => {
fs.readFile('./package/content.txt', (err, data) => {
// 如果出错
if(err) reject(err)
// 如果成功
resolve(data)
})
})
// then里面有两个回调函数
p.then(value => {
console.log(value.toString())
}, reason=> {
console.log(reason)
})
(2) Promise 封装 AJAX, 将用到一个开源 api地址:
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
#page {
font-size: 24px;
}
#btn {
background-color: blue;
}
</style>
</head>
<body>
<div class="container">
<h2 class="page">Promise 封装AJAX操作</h2>
<button class="btn" id="btn">点击发送</button>
</div>
<script>
// 接口地址: https://api.apiopen.top/getJoke
const btn = document.querySelector('#btn')
btn.addEventListener('click', function() {
// 创建Promise
const p = new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
// 初始化
xhr.open('GET', 'https://api.apiopen.top/getJoke')
// 发送
xhr.send()
// 处理响应结果
xhr.onreadystatechange = function() {
if(xhr.readyState ===4) {
// 判断响应状态码 2xx
if(xhr.status >=200 && xhr.status < 300){
// 成功,输出响应体
// console.log(xhr.response)
resolve(xhr.response)
}else{
// 失败
//console.log(xhr.status)
reject(xhr.status)
}
}
}
});
p.then(value =>{
console.log(value)
}, reason => {
console.log(reason)
})
})
</script>
</body>
</html>
当然也可以把AJAX的封装提取出来
function sendAjax(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)
}
}
}
})
}
(3) 封装一个 读取文件的函数
/**
* 封装一个函数 mineReadFile 读取文件内容
* 参数: path 文件路径
* 返回: promise 对象
*/
function mineReadFile(path) {
return new Promise((resolve, reject) => {
// 读取文件
require('fs').readFile(path, (err, data) => {
// 判断
if(err) reject(err);
// 成功
resolve(data)
})
})
}
mineReadFile('./package/content.txt')
.then(value =>{
console.log(value.toString())
},reason => {
console.log(reason)
})
也可以通过util.promisify():传入一个遵循常见的错误优先的回调风格的函数,并返回一个返回promise的版本
/**
* util.promisify 方法
*/
//引入util模块
const util = require('util')
const fs = require('fs')
// 返回一个新函数
let mineReadFile = util.promisify(fs.readFile);
mineReadFile('./package/content.txt').then(value => {
console.log(value.toString())
},reason => {
console.log(reason)
})
3, Promise 原理知识
(1)Promise的状态,实例对象中的一个属性:PromiseStatue, 一种有三只状态
-
pending 未决定的
-
resolved / fullfilled 成功
-
rejected. 失败
状态的改变只能有两种情况,一个promise 对象只能改变一次
1,pengding 变为resolved
2, pending 变为 rejected
(2)Promise 对象的值, 实例对象中的一个属性: PromiseResult,保存异步任务成功/失败的结果
1, resolve
2, reject
4, Promise 的基本流程
graph LR
B(new Promise/pendding状态)
B-->C{执行异步操作}
C-->|成功了执行/resolve |D(promise对象/resolved状态)
C-->|失败了执行/reject|E(promise对象/rejected状态)
D-->|then方法之后 |F(回调onResolved方法)
E-->|then/catch方法之后 |H(回调onRejected方法)
F-->J{新的promise}
H-->J{新的promise}
p.then(第一个成功的回调函数, 第二个失败的回调函数)
5, Promise API
(1) Promise 构造函数 Promise(executor{})
- executor 函数: 执行器,会在Promise内部立即同步调用
- resolve 函数: 内部定义,成功时调用的函数 value => {}
- reject 函数: 内部定义,失败的时调用的函数 reason => {}
(2) Promise.prototype.then 方法 (onResolved, onRejected) => {}
- onResolved 函数: 成功的回调函数 (value) => {}
- onRejected 函数 : 失败的回调函数 (reason)=>{}
(3)Promise.prototype. catch方法 (onRejected) => {}
只能在失败的时候,回调的函数
(4)Promise.resolve()
说明:Promise函数对象上的方法,和实例的方法不一样,
返回一个成功/失败的promise对象
let p1 = Promise.resolve(123)
// 如果传入的参数为非Promise类型的对象,则返回的结果为成功的promise对象
// 如果传入的参数为Promsie对象,则参数的结果决定了resolve的结果
let p2 = Promise.resolve(new Promise((resolve, reject) => {
resolve('ok')
}))
(5)Promise.reject方法
返回一个失败的promise对象
let p = Promise.reject(123)
(6) Promise.all方法: (promise)=>{}
说明:返回一个新的promise, 只有所有的promise都成功了才成功,只要有一个失败了就直接失败
let p1 = new Promise((resolve, reject) => {
resolve('ok')
})
let p2 = new Promise((resolve, reject) => {
resolve('ok')
})
let p3 = new Promise((resolve, reject) => {
resolve('ok')
})
const result = Promise.all([p1,p2,p3])
(7)Promise.race方法, 等同于Promise.all方法,不一样的地方:
成功或者失败的状态只能由第一个Promise的结果状态决定
const result = Promise.race([p1,p2,p3])
// 由定一个p1决定
注意1: 改变Promise的状态只有三只方式:
1, resolve()
2,reject()
3, 抛出错误: throw 'err'
注意2: 如果一个Promise指定多个成功/失败回调函数,当Promise改变对应状态时都会调用
注意3: 改变Promise状态和指定回调函数谁先谁后?
都有可能,正常情况下是先指定回调函数载改变状态,但是也可以先改变状态再指定回调
执行器函数里面是个异步任务的时候,先执行then, 后改变状态
注意4: promise的链式调用
then的返回也是promise对象
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) // 输出success
}).then(value => {
console.log(value) // 输出undefined
}).catch(reason => {
console.log(reason)
})
注意5: 异常穿透
多个.then()调用的时候,只需要在最后一个地方通过:catch() 捕获
注意6: 如何终止一个链式.then()的回调
有且只有一个方法: 返回一个 pendding状态的promise, 通过return true, throw 都不行的
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok')
}, 1000);
})
p.then(value => {
console.log(111)
return ; // 终止不掉的
}).then(value => {
console.log(222)
return new Promise(()=>{}) // 可以终止掉then的链式调用
}).then(value => {
console.log(333)
})
6, 总结
promise的理解很重要,后续会一遍学习,一遍封装一个自己的promise类库,加深自己对promise的理解