第一章 Promise 的理解
1.1 Promise 是什么
1.1.1 理解
- 抽象表达:
- Promise 是一门新的技术(ES6规范)
- Promise 是 JS 中进行异步编程的 新解决方案 ,旧解决方案是单纯使用回调函数
- 具体表达:
- 从语法上来说:Promise 是一个构造函数
- 从功能上来说:Promise 对象用来封装一个异步操作,并可以获取其 成功/失败 的结果值
1.1.2 应用于异步编程
- fs 文件操作
require('fs').readFile('./index.html', (err,data)=>{})
- 数据库操作
- AJAX
$.get('/server', (data)=>{})
- 定时器
setTimeout(()=>{}, 2000);
1.2 为什么要使用 Promise
1.2.1 指定回调函数的方式更加灵活
- 旧的:必须在启动异步任务前指定
- promise:启动异步任务 => 返回 promise 对象 => 给 promise 对象绑定回到函数(甚至可以在异步任务结束后指定多个)
1.2.2 支持链式调用,可以解决回调地狱问题
-
什么是回调地狱
回调函数的嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件
asyncFun1 (opt, (...arg1) => { asyncFun2 (opt, (...arg2) => { asyncFun3 (opt, (...arg3) => { // ... }) }) })
-
回调地狱的缺点
- 不便于阅读
- 不便于异常处理
-
解决方案
- promise 链式调用
1.3 Promise 的状态
实例对象中的一个属性 『PromiseState』
- pending 未决定的
- resolved / fulfilled 成功
- rejected 失败
- 说明:只有这三种状态,且一个 Promise 对象只能改变一次,无论变成成功还是失败,都会有一个结果数据,成功的结果数据一般称为 value,失败的结果数据一般称为 reason
1.4 Promise 对象的值
实例对象中的另一个属性 『PromiseResult』
保存着异步任务『成功/失败』的结果
- resolve
- reject
第二章 Promise 运用
2.1 API
-
Promise 构造函数:Promise (executor) {}
- executor 函数:执行器 (resolve, reject) => {}
- resolve 函数:内部定义成功时调用的函数 value => {}
- reject 函数:内部定义失败时调用的函数 reason => {}
说明:executor 会在 Promise 内部立即同步调用,异步操作在执行器中执行
let p = new Promise ((resolve, reject) => { console.log(111) // 这行代码同步执行 resolve('ok') }) console.log(222) // 先输出 111 然后 输出 222
-
Promise.prototype.then 方法:(onResolved, onRejected) => {}
- onResolved 函数:成功的回调函数 (value) => {}
- onRejected 函数:失败的回调函数 (reason) => {}
说明:指定用于得到成功 value 的成功回调和用于得到失败 reason 的失败回调,返回一个新的 promise 对象
-
Promise.prototype.catch 方法:(onRejected) => {}
- onRejected 函数:失败的回调函数 (reason) => {}
-
Promise.resolve 方法:(value) => {}
- value:成功的数据或 promise 对象
说明:返回一个成功/失败的 promise 对象
// 如果传入的参数为 非Promise 类型的对象,则返回的结果为成功的 promise 对象 const p = Promise.resolve('ok') p.then(value => { console.log(value) }) // 如果传入的参数为 Promise 对象,则参数的结果决定了 resolve 的结果 const p1 = Promise.resolve( new Promise((resolve, reject) => { reject('error') }) ) p1.catch(reason => { console.log(reason) })
-
Promise.reject 方法:(reason) => {}
- reason:失败的原因
说明:返回一个失败的 promise 对象,无论参数是普通的字符串还是一个成功的 promise 对象,其返回结果均是一个失败的 promise 对象
-
Promise.all 方法:(promises) => {}
- promises:包含 n 个 promise 的数组
说明:返回一个新的 promise,只有所有的 promise 都成功才成功,只要有一个失败了就直接失败
let p1 = new Promise((resolve, reject) => { resolve('ok') }) let p2 = Promise.resolve('nice') let p3 = Promise.resolve('oh') let p = Promise.all([p1, p2, p3]) console.log(p) // [[PromiseState]]: "fulfilled" [[PromiseResult]]: Array(3) let p4 = Promise.reject('err') let pe = Promise.all([p1, p2, p3, p4]) console.log(pe) // [[PromiseState]]: "rejected" [[PromiseResult]]: "err"
-
Promise.race 方法:(promises) => {}
- promises:包含 n 个 promise 的数组
说明:返回一个新的 promise,第一个完成的 promise 的结果状态就是最终的结果状态
let p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('ok') }, 1000) }) let p2 = Promise.resolve('nice') let p3 = Promise.resolve('oh') let p = Promise.race([p1, p2, p3]) console.log(p) // nice
2.2 Promise 的问题
- 改变 Promise 对象的方法
let p1 = new Promise((resolve, reject) => {
// 1. resolve 函数
// resolve('ok') // pending => fulfilled (resolved)
// 2. rejected 函数
// reject('err') // pending => rejected
// 3. 抛出错误
throw 'error'
})
console.log(p1)
-
一个 promise 指定多个 成功/失败 回调函数,都会调用吗?
- 当 promise 改变为对应状态时都会调用
-
改变 promise 状态和指定回调函数谁先谁后?
- 都有可能,正常情况下是先指定回调再改变状态,但也可以先改变状态再指定回调
- 如何先改状态再指定回调?
- 在执行器中直接调用 resolve() / reject()
- 延迟更长时间才调用 then() (定时器或者ajax请求)
- 什么时候才能得到数据?
- 如果先指定的回调,那当状态发生改变时,回调函数就会调用,得到数据
- 如果先改变状态,那当指定回调时,回调函数就会调用,得到数据
补充:微任务和宏任务,微任务先执行,宏任务后执行
- 宏任务(macrotask):
- 异步 Ajax 请求、
- setTimeout、setInterval、
- 文件操作
- 微任务(microtask):
- Promise.then、.catch 和 .finally
- process.nextTick
- 其它微任务
-
promise.then() 返回的新 promise 的结果状态由什么决定?
- 简单表达:由 then() 指定的回调函数执行的结果决定
- 详细表达:
- 如果输出异常,新 promise 变为 rejected,reason 为抛出的异常
- 如果返回的是非 promise 的任意值,新 promise 变为 resolved,value 为返回的值
- 如果返回的是另一个新 promise,此 promise 的结果就会成为新 promise 的结果
-
promise 如何串联多个操作任务?
-
promise 的 then() 返回一个新的 promise,可以变成 then() 的链式调用
-
通过 then 的链式调用串联多个同步/异步任务
-
let p = new Promise((resolve, reject) => { setTimeout(() => { resolve(200) }, 1000) }) let result = p.then(value => { console.log(value) // 200 return new Promise((resolve, reject) => { resolve('success') }) }).then(value => { console.log(value) // success }).then(value => { console.log(value) // undefined }) console.log(result)
-
-
promise 异常穿透?
-
当使用 promise 的 then 链式调用时,可以在最后指定失败的回调
-
前面任何操作出了异常,都会传到最后失败的回调中处理
-
let p = new Promise((resolve, reject) => { setTimeout(() => { // reject('出错了') resolve('200') }, 1000) }) p.then(value => { console.log(value) // 200 return new Promise((resolve, reject) => { reject('出错了') }) }).then(value => { console.log(value) }).then(value => { console.log(value) }).catch(err => { console.warn(err) // '出错了' })
-
-
中断 promise 链?
-
当使用 promise 的 then 链式调用时,在中间中断,不再调用后面的回调函数
-
办法:在回调函数中返回一个 pending 状态的 promise 对象
-
let p = new Promise((resolve, reject) => { setTimeout(() => { resolve('200') }, 1000) }) p.then(value => { console.log(value) // 200 return new Promise(() => {}) // 下面的then都不再执行 }).then(value => { console.log(value) }).then(value => { console.log(value) }).catch(err => { console.warn(err) })
-
2.3 提前结束promise
<body>
<button>发起</button>
<button>取消</button>
<script>
const obj = {}
let p
let button = document.getElementsByTagName('button')[0]
button.onclick = async function () {
console.log('5s后自动执行成功')
p = new Promise((resolve, reject) => {
obj.reject = reject
setTimeout(() => {
resolve(123)
}, 5000)
}).then(value => {
console.log(value)
}, reason => {
console.warn(reason)
})
}
let cancelButton = document.getElementsByTagName('button')[1]
cancelButton.onclick = async function () {
obj.reject('提前执行reject')
}
</script>
</body>
第三章 Promise 原理
class Promise {
// 构造方法
constructor(executor) {
// 初始化 PromiseState 和 PromiseResult 属性
this.PromiseState = 'pending'
this.PromiseResult = null
// 保存回调函数
this.callbacks = []
// 保存实例对象的 this 的值
const self = this
// resolve 函数
function resolve (data) {
// 执行以下代码前判断是否已经修改过 promise 的状态了,如果修改过则不能再修改
if (self.PromiseState !== 'pending') return
// 1、修改对象的状态(promiseState)
self.PromiseState = 'fulfilled'
// 2、修改对象的结果(promiseResult)
self.PromiseResult = data
// 调用成功的回调
setTimeout(() => {
self.callbacks.forEach(item => {
item.onResolved(data)
})
});
}
// reject 函数
function reject (data) {
// 执行以下代码前判断是否已经修改过 promise 的状态了,如果修改过则不能再修改
if (self.PromiseState !== 'pending') return
// 1、修改对象的状态(promiseState)
self.PromiseState = 'rejected'
// 2、修改对象的结果(promiseResult)
self.PromiseResult = data
// 调用成功的回调
setTimeout(() => {
self.callbacks.forEach(item => {
item.onRejected(data)
})
})
}
try {
// 异步调用【执行器】函数
setTimeout(() => {
executor(resolve, reject)
})
} catch (error) {
// 捕获错误,修改 promise 对象状态为【失败】
reject(error)
}
}
// then 方法
then (onResolved, onRejected) {
let self = this
// 判断回调函数的参数是否存在
// onRejected 如果没有传递,则为undefined,在实现catch穿透时会出错,所以没有传递时要初始化
if (typeof onRejected !== 'function') {
onRejected = reason => {
throw reason
}
}
// onResolved 如果没有传递,则为undefined,在实现.then值传递时会出错,所以没有传递时要初始化
if (typeof onResolved !== 'function') {
onResolved = value => value
}
return new Promise((resolve, reject) => {
// 处理相同功能的函数
function callFun (funType) {
try {
let result = funType(self.PromiseResult)
if (result instanceof Promise) {
result.then(v => {
resolve(v)
}, r => {
reject(r)
})
} else {
resolve(result)
}
} catch (e) {
reject(e)
}
}
// 调用回调函数
if (this.PromiseState === 'fulfilled') {
setTimeout(() => {
callFun(onResolved)
})
}
if (this.PromiseState === 'rejected') {
setTimeout(() => {
callFun(onRejected)
})
}
// 如果状态为 pending,则说明有可能是个异步任务
// 避免后面有执行 resolve 或者 reject,将回调保存起来
if (this.PromiseState === 'pending') {
this.callbacks.push({
onResolved: function () {
callFun(onResolved)
},
onRejected: function () {
callFun(onRejected)
}
})
}
})
}
// catch 方法
catch (onRejected) {
return this.then(undefined, onRejected)
}
// resolve 方法
static resolve (value) {
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 方法,全部都为fulfilled才会返回所有结果,其中有rejected则返回reject的结果
static all (promises) {
return new Promise((resolve, reject) => {
// 用于记录成功的条数
let count = 0
// 用于保存成功的promise对象
let arr = []
for (let i = 0; i < promises.length; i++) {
if (promises[i] instanceof Promise) {
promises[i].then(v => {
// 对象的状态是成功的走这里
count++
arr[i] = v
// 当所有promise对象都成功才可以resolve
if (count === promises.length) {
resolve(arr)
}
}, r => {
// 所有对象中有一条失败就直接reject
reject(r)
})
} else {
count++
arr[i] = v
if (count === promises.length) {
resolve(arr)
}
}
}
})
}
// race 方法 ,哪个执行快就返回哪个,无论是fulfilled还是rejected
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)
})
}
})
}
// 只要有一个的执行结果是fulfilled,就会返回,如果全为rejected,都结束后reject返回AggregateError: All promises were rejected
static any (promises) {
return new Promise((resolve, reject) => {
let count = 0
promises.forEach((item, index) => {
item.then(v => {
resolve(v)
}, r => {
count++
if (count === promises.length) {
reject('AggregateError: All promises were rejected')
}
})
})
})
}
// allSettled 方法
static allSettled (promises) {
return new Promise((resolve, reject) => {
let count = 0
let result = []
promises.forEach((item, index) => {
const obj = {}
count++
item.then(v => {
obj.status = 'fulfilled'
obj.value = v
}, r => {
obj.status = 'rejected'
obj.reason = r
})
result[index] = obj
if (count === promises.length) {
resolve(result)
}
})
})
}
}
第四章 async 与 await
4.1 async 函数
-
函数的返回值为 promise 对象
-
promise 对象的结果由 async 函数执行的返回值决定
-
async function fun () { return Promise.reject(321) } let res = fun() console.log(res)
4.2 await 表达式
-
await 右侧的表达式一般为 promise 对象,但也可以是其他的值
-
如果表达式是 promise 对象,await 返回的是 promise 成功的值
-
如果表达式是其他值,直接将此值作为 await 的返回值
-
async function fun1 () { const p = new Promise((resolve, reject) => { resolve(123) }) // 1. 右侧为 promise 的情况 let res = await p console.log(res) // 123 // 2. 右侧为 非promise 的情况 let res2 = await 321 console.log(res2) // 321 // 3. 右侧为失败的 promise 的情况 const p2 = Promise.reject('error') try{ let res3 = await p2 } catch(e) { console.warn(e) } } fun1()
4.3 注意
- await 必须写在 async 函数中,但 async 函数中可以没有 await
- 如果 await 的 promise 失败了,就会抛出异常,需要通过 try…catch 捕获处理
第五章 Promise 的演化
5.1 util.promisify
/**
* util.promisify 方法
*/
// 引入 util 模块
const util = require('util')
// 引入 fs 模块
const fs = require('fs')
// 返回一个 promise 类型的函数
let myReadFile = util.promisify(fs.readFile)
// 使用
myReadFile('./test.md')
.then(res => {
console.log(res.toString())
})
.catch(err => {
console.log(err)
})