先说一下什么是回调函数
回调函数就是一个函数a作为实参传入另一个函数b,并在b中执行a这个函数,这就是回调函数,代码形式如下
function b(callback){
console.log('我是b的打印')
callback()
}
b(function(){
console.log('我是a的打印')
})
//执行结果是:我是b的打印 我是a的打印
如上代码,function(){console.log('我是a的打印')} ,这个函数就是一个回调函数
当然了,回调函数也可以传参,比如
function a(callback)
{
let data
setTimeout(() => {
data = "我是数据"
}, 2000)
callback(data)
}
a((a) => {
console.log(a) //我是数据
})
通过这种方法,很明显。我们可以通过回调函数进行异步操作。
但是如果多层嵌套,就会造成回调地狱问题
- 代码难看
- 难以理解和维护
- 捕获异常要用try catch,比较臃肿
Promise
它代表了一个尚未完成但预期在将来完成的操作。使用Promise,可以避免所谓的“回调地狱”。
executor中执行异步操作,利用then函数回传
new Promise( function(resolve, reject) { /* executor */
// 执行代码 需要指明resolve与reject的回调位置
});
//executor是带有resolve和reject两个参数的函数。Promise构造函数执行时立即调用executor函数,resolve和reject两个函数作为参数传递给executor。
promise有三种状态
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
Promise对象只有从pending变为fulfilled和从pending变为rejected的状态改变。只要处于 fulfilled 和 rejected ,状态就不会再变了。
优点:
- 通过链式调用,避免了深度嵌套的回调函数,使得代码更加清晰易读。
- Promise实例之间可以轻松组合和复用,使得代码更加模块化和灵活。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果
缺点:
- Promise对象一旦创建后,其状态就不可再改变。这意味着无法取消或者终止Promise实例的执行。
- 虽然Promise内置了错误处理机制,但有时错误处理可能不够直观,特别是在处理多个并发Promise时,可能会出现不易定位和调试的问题。
- 当处于 pending 状态时,无法得知目前进展到哪一个阶段
手写Promise
参考: 面试官:请手写一个Promise
首先实现一下简单的Promise
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
export class Promise {
constructor(executor) {
this.value = undefined
this.reason = undefined
this.status = PENDING
const resolve = (value) => {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled && onFulfilled(this.value) //如果noFulfilled不为空,那就那就执行,为空的话由于是&&,就不会调用了
}
if (this.status === REJECTED) {
onRejected && onRejected(this.reason)
}
}
}
这个初始完成的Promise比较简单,可以自己起个node试用一下
上面我们写的Promise有一个问题,那就是只能处理同步操作
比如我们这样
import { Promise } from './Promise.js'
const promise = new Promise((resolve, reject) => {
//传入回调函数,接收Promise的resolve函数和reject函数
setTimeout(() => {
const i = 3
resolve(i)
}, 2000)
})
promise.then((value) => {
console.log(value)
})
//这样就不会返回任何值,因为在调用then函数的时候,Promise还没有改变状态,而且我们目前的then函数中对于没有改变状态,也就是pending状态的Promise没有做出响应。
改进(实现异步):
在then方法调用时,如果Promise还处在pending状态,那我们就先法then中传入的回调函数存起来,等到Promise改变状态之后(也就是调用resolve或reject后)再一并执行。这样也就实现了异步。
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
export class Promise {
constructor(executor) {
this.value = undefined
this.reason = undefined
this.status = PENDING
this.onFulfilledCallbacks = [] //存储异步操作成功之后的回调函数
this.onRejectedCallbacks = [] // 存储异步操作失败之后的回调函数
const resolve = (value) => {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onFulfilledCallbacks.forEach((callback) => callback(this.value)) //统一执行
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onResolvedCallbacks.forEach((callback) => callback(this.value)) //统一执行
}
}
executor(resolve, reject)
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled && onFulfilled(this.value) //如果noFulfilled不为空,那就那就执行,为空的话由于是&&,就不会调用了
} else if (this.status === REJECTED) {
onRejected && onRejected(this.reason)
} else { //如果时pending状态就先存起来
if (onFulfilled) this.onFulfilledCallbacks.push(onFulfilled)
if (onRejected) this.onRejectedCallbacks.push(onFulfilled)
}
}
}
改进(实现链式调用)
非常巧妙,读了半天才懂
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
export class Promise {
constructor(executor) {
this.value = undefined
this.reason = undefined
this.status = PENDING
this.onResolvedCallbacks = [] //存储异步操作成功之后的回调函数
this.onRejectedCallbacks = [] // 存储异步操作失败之后的回调函数
const resolve = (value) => {
if (this.status === PENDING) {
this.value = value
this.status = FULFILLED
this.onResolvedCallbacks.forEach((callback) => callback())
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedCallbacks.forEach((callback) => callback())
}
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
// 改良链式调用的then有两种作用:1. 如果onFulfilled或者onRejected返回的是一个真正的Promise的时候,将这个Promise返回2. 如果不是的话(假设return x),要返回一个Fulfilled状态的Promise对象,并将这个值传到返回的Promise中的value中去
then(onFulfilled, onRejected) {
// 关键的函数 新Promise return值 新Promise的resolve和reject
const resolvePromise = (promise2, x, resolve, reject) => {
//这段代码的目的是防止 Promise 链中出现循环引用导致的死循环。在 Promise 的链式调用中,如果某个 Promise 的 then 方法返回了它本身,就会形成一个循环引用,导致 Promise 链无法正常结束,进而造成死循环。
if (promise2 === x) {
return reject(new TypeError('UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #<Promise>'))
}
let called = false
// 判断x的类型 x是对象或函数才有可能是一个promise
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
const then = x.then
if (typeof then === 'function') {
// 只能认为它是一个promise
then.call(
x,
(y) => {
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject) //递归处理,直到x不是promise或者余姚reject
},
(r) => {
if (called) return
called = true
reject(r)
}
)
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
//如果返回的数不是Promise 那就直接调用resolve函数将Promise2变为Fulfilled的然后返回
console.log('我这里变成了Fulfilled')
resolve(x)
}
}
const promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
// onFulfilled方法可能返回值或者promise
console.log('我是fulfilled')
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
}
if (this.status === REJECTED) {
// onRejected方法可能返回值或者promise
console.log('我是rejected')
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
const x = onFulfilled(this.value)
console.log('x', x, typeof x)
resolvePromise(promise2, x, resolve, reject)
})
this.onRejectedCallbacks.push(() => {
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
})
}
})
return promise2
}
}
然后再在一些关键的地方增加js提供的调度微任务的函数就可以啦
const promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
// onFulfilled方法可能返回值或者promise
queueMicroTask(() => {
try {
console.log('我是fulfilled')
const x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === REJECTED) {
// onRejected方法可能返回值或者promise
queueMicroTask(() => {
try {
console.log('我是rejected')
const x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.status === PENDING) {
queueMicroTask(() => {
try {
this.onResolvedCallbacks.push(() => {
const x = onFulfilled(this.value)
console.log('x', x, typeof x)
resolvePromise(promise2, x, resolve, reject)
})
} catch (e) {
reject(e)
}
})
}
})