一、Promise的基本介绍与使用
1、异步编程
- fs文件操作
- 数据库操作
- ajax
- 定时器
在promise之前都是使用回调函数,但是存在回调地狱问题
1、promise支持链式调用
,可以有效解决回调地狱的问题
2、指定回调函数的方式更加灵活
2、promise的基本使用
const p = new Promise((resolve,reject)=>{
// 此时状态为pending
if(成功) {
resolve()
}else {
reject()
}
})
// 分别接收成功和失败的回调
p.then(()=>{},()=>{})
3、util.promisify方法
传入一个遵循错误有线的回调风格的函数(err,data)=>{}
,最终返回promise版本
const util = require('util')
const fs = require('fs')
// 返回一个新的函数
let mineReadFile = util.promisify(fs.readFile)
mineReadFile('./file/index.txt').then(value=>{
console.log(value)
})
4、Promise的状态
该状态存储在Promise实例对象中的属性中【PromiseState】
pending
未决定的
resolved/fulfilled
成功
rejected
失败
5、Promise对象的值
实例对象中的另一个值【PromiseResult】,用于储存成功或者失败的结果
resolve()
和reject()
两个函数都可以修改这个值
通过.then
可以取出该值
6、Promise的工作流程
二、Promise API
1、Promise构造函数Promise(executor){}
- executor函数:执行器
(resolve,reject)=>{}
- resolve函数:内部定义成功时调用
value=>{}
- reject函数:内部定义失败时调用
reason=>{}
executor会在Promise内部立即同步调用,异步操作会在执行器中执行
2、Promise.prototype.then方法
(onResolved,onRejected)=>{}
- onResolved函数:成功的回调函数
(value)=>{}
- onRejected函数:失败的回调函数
(reason)=>{}
3、Promise.prototype.catch方法
(onRejected)=>{}
- onRejected函数:失败的回调函数
(reason)=>{}
4、Promise.resolve方法
(value)=>{}
- value:成功的数据或Promise对象
返回一个成功/失败的Promise对象
let p = Promise.resolve('ok')
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: 'ok'
let p2 = Promise.resolve(new Promise((resolve,reject)=>{}))
规律:- 如果传入的是非Promise类型的对象,返回结果为rejected
的Promise对象
- 如果传入一个Promise对象,则结果状态由参数结果状态决定
5、Promise.reject方法
(reason)=>{}
- reason:失败的原因
无论传入什么类型的参数都返回一个失败的promise对象
6、Promise.all方法
([promise])=>{}
- [promise]:一个包含多个Promise对象的数组
结果返回一个新的Promise,数组中所有的Promise成功才返回成功,反之失败
let p1 = Promise.resolve('ok')
let p2 = Promise.resolve('ok')
let p3 = Promise.resolve('ok')
let result = Promise.all([p1, p2, p3])
/** [[PromiseState]]: "fulfilled"
* [[PromiseResult]]: Array(3) - 成功返回结果是三个Promise对象结果的数组
* - 失败结果直接是"Error"
*/
7、Promise.race方法
([promise])=>{}
结果返回一个新的Promise,结果以数组中第一个返回的Promise结果为准
三、Promise关键问题
1、如何修改Promise的状态
- resolve(value)将pending改为resolved
- reject(reason)将pending改为rejected
- 抛出异常可以将pending改为rejected
throw err
2、如果使用then方法给一个Promise对象指定多个回调,是否都会调用
- 当Promise状态改变后都会调用
let p = Promise.resolve('ok')
// 两个都会执行
p.then((value) => { console.log(value) }) // ok
p.then((value) => { console.log(value) }) // ok
3、改变Promise状态和指定回调函数先后问题
两种情况都有可能:
- 执行器中
直接改变状态
或者.then的调用是延时的
都可以先改变状态 - 如果执行器中的代码是异步的,会先指定回调后改变状态(多数)
4、Promise.then()返回的Promsise结果由什么决定
let p = new Promise((resolve,reject)=>{
resolve('ok')
})
let result = p.then(value=>{},reason=>{})
console.log(result)
// result结果是一个Promise对象,结果由回调函数执行结果决定
5、Promise如何串联多个操作任务
- 通过.then返回新的Promise,可以进行链式调用
- 通过链式调用实现了串联
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok1')
}, 1000)
})
p.then(value => {
return new Promise((resolve, reject) => {
resolve('ok2')
})
}).then(value => {
console.log(value) // ok2
})
6、Promise的异常穿透
当使用链式调用的时候,只需要在最后使用.catch
指定失败的回调,前边任何操作出现失败都会在最后进行处理
7、中断Promise链
在链式调用中想要中断这个链式调用,不再调用后面的回调函数:
返回一个状态为pending的Promise对象return new Promise(()=>{})
四、Promise自定义封装
1、简单实现一个同步功能
// promise.js
function Promise(executor) {
const self = this
// Promise状态
this.PromiseState = 'pending'
// Promise的结果
this.PromiseResult = null
function resolve(data) {
// 保证状态只能修改一次
if (self.PromiseState !== 'pending') return
// 1、修改对象的状态(PromiseState)
self.PromiseState = 'fulfilled'
// 2、设置结果值(PromiseResult)
self.PromiseResult = data
}
function reject(data) {
if (self.PromiseState !== 'pending') return
self.PromiseState = 'rejected'
self.PromiseResult = data
}
try {// 添加错误捕获实现throw修改状态
// 同步调用执行器函数
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
Promise.prototype.then = function (onResoleved, onRejected) {
// 调用回调函数
if (this.PromiseState === 'fulfilled') {
onResoleved(this.PromiseResult)
}
if (this.PromiseState === 'rejected') {
onRejected(this.PromiseResult)
}
}
// 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>
</head>
<body>
<script src="./promise.js"></script>
<script>
let p = new Promise((resolve, reject) => {
resolve('ok')
reject('err') // 不执行
throw 'err' // 不执行
}).then(value => {
console.log(value) // ok
}, reason => {
console.log(reason)
})
</script>
</body>
</html>
2、如果执行器函数中存在异步操作,导致状态修改延迟,则无法执行回调
在.then方法中
对异步进行处理(对处于pending的状态进行处理)
- 同步任务回调是在执行then方法调用时执行的
- 异步任务回调的执行要放在状态修改后执行(resolve和reject中)
如果执行then方法时,状态没有发生修改,就不会执行回调函数,应将回调函数保存
—— 此时声明一个对象保存异步待处理回调函数 this.callback={}
Promise.prototype.then = function (onResoleved, onRejected) {
...
if(this.PromiseState === 'pending'){
this.callback = { onResoleved, onRejected }
}
}
—— 当修改状态后判断,then是否已经调用过了,是否需要自动执行回调
function resolve(data) {
// ... 修改状态操作
if (self.callback.onResoleved) {
self.callback.onResoleved(data)
}
}
function reject(data) {
// ... 修改状态操作
if (self.callback.onRejected) {
self.callback.onRejected(data)
}
}
—— 运行结果
<body>
<script src="./promise.js"></script>
<script>
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('ok')
}, 1000)
}).then(value => {
console.log(value) // 一秒后ok
}, reason => {
console.log(reason)
})
</script>
</body>
3、现在的封装还无法实现异步指定多个回调
因为通过callback对象
保存回调函数,如果有多个回调函数,后一个会覆盖前一个
改进:通过数组保存回调函数
// 声明一个数组保存异步待处理回调函数
this.callback = []
// 将待保存回调函数对象添加到数组中
Promise.prototype.then = function (onResoleved, onRejected) {
...
if (this.PromiseState === 'pending') {
// 此时状态不确定,不能调用回调函数,暂时保存回调函数
this.callback.push({ onResoleved, onRejected })
}
}
// 状态修改完毕后遍历数组执行回调
function resolve(data) {
...
setTimeout(() => {
self.callback.forEach(item => {
item.onResoleved(data)
})
})
}
function reject(data) {
...
setTimeout(() => {
self.callback.forEach(item => {
item.onRejected(data)
})
})
}
4、处理then方法返回的结果
此时then方法可以调用回调函数,但是自身没有返回值
要实现then的结果返回一个Promise,状态由回调函数的结果决定
let result = p.then(value => {
return value
})
console.log(result) // undefined
给then方法添加return new Promise
Promise.prototype.then = function (onResoleved, onRejected) {
return new Promise((resolve, reject) => {
// 调用回调函数
if (this.PromiseState === 'fulfilled') {
try {
// 接收回调函数的结果作为then函数的结果(Promise||非Promise)
let result = onResoleved(this.PromiseResult)
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {// 不是Promise对象,状态为rejected
resolve(result)
}
} catch (err) {
resolve(err)
}
}
})
}
<body>
<script src="./promise.js"></script>
<script>
let p = new Promise((resolve, reject) => {
resolve('ok')
})
let result = p.then(value => {
return new Promise((resolve, reject) => {
throw 'err'
// resolve('success') throw|reject|resolve都能修改状态
})
})
console.log(result)
</script>
</body>
rejected状态进行同样的处理
5、异步状态下then返回结果的实现
当修改状态是一个异步的操作,执行then方法会暂时存储回调函数
将会回调函数的存储再次进行改进,在保存的回调函数中修改then返回值的状态
Promise.prototype.then = function (onResoleved, onRejected) {
const self = this
return new Promise((resolve, reject) => {
...
if (this.PromiseState === 'pending') {
// 此时状态不确定,不能调用回调函数,暂时保存回调函数
this.callback.push({
onResoleved: function () {
try {
// 执行回调函数,并获取返回值
let result = onResoleved(self.PromiseResult)
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(result)
}
} catch (err) {
reject(err)
}
},
onRejected: function () {
try {
let result = onRejected(self.PromiseResult)
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(result)
}
} catch (err) {
reject(err)
}
}
})
}
})
}
执行结果
<body>
<script src="./promise.js"></script>
<script>
let p = new Promise((resolve, reject) => {
setTimeout(()=>{
resolve('ok')
},1000)
})
let result = p.then(value => {
return new Promise((resolve, reject) => {
throw 'err' // 这个状态的修改影响到了then的返回值状态
})
})
console.log(result) // rejected状态
</script>
</body>
6、代码优化
- 经过分析可以看出以上
try{}catch(err){}
中存在大量重复代码,需要进行封装 - 添加定时器实现回调异步执行
- 对未传入回调函数的情况进行初始化
Promise.prototype.then = function (onResoleved, onRejected) {
// 未传入回调,初始化
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason
}
}
if (typeof onResoleved !== 'function') {
onRejected = value => value
}
const self = this
return new Promise((resolve, reject) => {
// 方法的封装
function callback(type) {
try {
// 接收回调函数的结果作为then函数的结果(Promise||非Promise)
let result = type(self.PromiseResult)
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {// 不是Promise对象,状态为rejected
resolve(result)
}
} catch (err) {
resolve(err)
}
}
/*---------------------------------------------------*/
// 调用回调函数
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onResoleved)
})
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected)
})
}
if (this.PromiseState === 'pending') {
// 此时状态不确定,不能调用回调函数,暂时保存回调函数
this.callback.push({
onResoleved: function () {
callback(onResoleved)
},
onRejected: function () {
callback(onRejected)
}
})
}
})
}
7、catch方法的封装
// catch方法,调用then方法的基础上只传入第二个回调
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
8、Promise.resolve的实现
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(value)
}
})
}
let p = Promise.resolve('ok')
console.log(p)
传入非Promise,状态为成功,传入Promise,结果以Promise结果为准
9 、Promise.reject的实现
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
无论传入什么,结果都是一个状态为rejected(失败)的Promise
10、Promise.all的实现
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
if (!(promises instanceof Array)) {
reject(promises + ' is not an array')
}
let count = 0
let successArr = []
// 遍历
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
count++
successArr[i] = v
if (count === promises.length) {
resolve(successArr)
}
}, r => {
reject(r)
})
}
})
}
运行结果
let p1 = Promise.resolve('1')
let p2 = Promise.resolve('2')
let p3 = Promise.resolve('3')
let p4 = Promise.reject('4')
console.log(Promise.all([p1, p2, p3]))
console.log(Promise.all([p1, p2, p3, p4]))
11、Promise.race方法的实现
返回的Promise的状态由数组中最先返回的Promise状态决定
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
if (!(promises instanceof Array)) {
reject(promises + ' is not an array')
}
// 遍历
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
resolve(v)
}, r => {
reject(r)
})
}
})
}
五、完整代码
function Promise(executor) {
const self = this
// Promise状态
this.PromiseState = 'pending'
// Promise的结果
this.PromiseResult = null
// 声明一个数组保存异步待处理回调函数
this.callback = []
function resolve(data) {
// 保证状态只能修改一次
if (self.PromiseState !== 'pending') return
// 1、修改对象的状态(PromiseState)
self.PromiseState = 'fulfilled'
// 2、设置结果值(PromiseResult)
self.PromiseResult = data
// 3、判断callback是否存入了回调函数,从而自动执行回调
setTimeout(() => {
self.callback.forEach(item => {
item.onResoleved(data)
})
})
}
function reject(data) {
if (self.PromiseState !== 'pending') return
self.PromiseState = 'rejected'
self.PromiseResult = data
setTimeout(() => {
self.callback.forEach(item => {
item.onRejected(data)
})
})
}
try {// 添加错误捕获实现throw修改状态
// 同步调用执行器函数
executor(resolve, reject)
} catch (err) {
reject(err)
}
}
// then方法
Promise.prototype.then = function (onResoleved, onRejected) {
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason
}
}
if (typeof onResoleved !== 'function') {
onRejected = value => value
}
const self = this
return new Promise((resolve, reject) => {
// 方法的封装
function callback(type) {
try {
// 接收回调函数的结果作为then函数的结果(Promise||非Promise)
let result = type(self.PromiseResult)
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {// 不是Promise对象,状态为rejected
resolve(result)
}
} catch (err) {
resolve(err)
}
}
/*---------------------------------------------------*/
// 调用回调函数
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callback(onResoleved)
})
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callback(onRejected)
})
}
if (this.PromiseState === 'pending') {
// 此时状态不确定,不能调用回调函数,暂时保存回调函数
this.callback.push({
onResoleved: function () {
callback(onResoleved)
},
onRejected: function () {
callback(onRejected)
}
})
}
})
}
// catch方法
Promise.prototype.catch = function (onRejected) {
return this.then(undefined, onRejected)
}
// Promise.resolve方法
Promise.resolve = function (value) {
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(value)
}
})
}
// Promise.reject方法
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
// Promise.all方法
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
if (!(promises instanceof Array)) {
reject(promises + ' is not an array')
}
let count = 0
let successArr = []
// 遍历
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
count++
successArr[i] = v
if (count === promises.length) {
resolve(successArr)
}
}, r => {
reject(r)
})
}
})
}
// Promise.race方法的实现
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
if (!(promises instanceof Array)) {
reject(promises + ' is not an array')
}
// 遍历
for (let i = 0; i < promises.length; i++) {
promises[i].then(v => {
resolve(v)
}, r => {
reject(r)
})
}
})
}
文章评论