1. 异步回调
1.1 回调地狱
在执行多个操作时,回调函数的嵌套可能会导致代码难以理解,这种现象通常被称为回调地狱
1.2 并行结果
当多个异步操作之间没有顺序要求时,可以并行执行以节省时间。然而,当必须等到所有异步操作完成后才能执行后续任务时,通常难以实现并行执行。
2. Promise
Promise的本意是承诺,在程序中表示一段时间后将会给出结果。什么时候需要‘等待一段时间’?通常是在处理异步操作时,比如网络请求或读取本地文件
3. Promise的三种状态
- Pending 实例创建时的初始状态
- Fulfilled 成功状态
- Rejected 失败状态
Promise 对象有三种状态:
Pending
(初始状态),Fulfilled
(成功状态),和Rejected
(失败状态)。当状态改变时,then
方法可以指定相应的回调函数,resolve
用于执行第一个函数(onFulfilled
),而reject
则执行第二个函数(onRejected
)。
4. 构建一个Promise
js
let promise = new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve("成功");
} else {
reject("失败");
}
}, 1000);
});
promise.then((data) => {
console.log(data);
});
- 构造一个 Promise 实例时,需要传入一个包含两个函数参数的回调函数
- 传入的回调函数需要有两个参数,通常被命名为
resolve
和reject
,它们分别表示操作成功和失败后的处理逻辑。resolve
使 Promise 状态变为成功,同时传递成功后的结果;reject
则使 Promise 状态变为失败,并传递错误信息。
5. Promise的链式调用
- 每次调用
then
都会返回一个新的 Promise 实例,这使得链式调用成为可能。链式调用中,前一个then
返回的值会作为参数传递给下一个then
方法。
js
readFile('1.txt').then(function (data) {
console.log(data);
return data;
}).then(function (data) {
console.log(data);
return readFile(data);
}).then(function (data) {
console.log(data);
}).catch(function(err){
console.log(err);
});
6. Promise API
Promise.all
Promise.all
方法接收一个包含多个 Promise 实例的数组,并返回一个新的 Promise 实例。返回的 Promise 状态取决于数组中所有 Promise 实例的状态。当所有实例都处于resolve
状态时,返回的 Promise 也会变为resolve
状态;如果任意一个实例处于reject
状态,返回的 Promise 也会变为reject
状态
js
Promise.all([p1, p2]).then(function (result) {
console.log(result); // [ '2.txt', '2' ]
});
// 不管两个promise谁先完成,Promise.all方法会按照数组里面的顺序将结果返回
Promise.race
Promise.race
方法接收一个包含多个 Promise 实例的数组,并返回一个新的 Promise 实例。返回的 Promise 状态取决于第一个改变状态的实例。当任意一个实例变为resolve
时,返回的 Promise 也会变为resolve
状态;如果任意一个实例变为reject
,返回的 Promise 也会变为reject
状态
js
Promise.race([p1, p2]).then(function (result) {
console.log(result); // [ '2.txt', '2' ]
});
Promise.resolve
Promise.resolve
方法返回一个resolve
状态的 Promise 实例。根据传入参数的不同,该方法可能会直接返回传入的值,或者是一个已经resolve
的 Promise 实例
Promise.reject
Promise.reject
方法返回一个reject
状态的 Promise 实例。通常会将抛出的错误信息作为参数传递给这个实例。
7. Promise/A+完整实现
tsx
// TypeScript实现
export class PfPromise {
private status: 'pending' | 'resolved' | 'rejected' = 'pending'
private value: any
private onResolvedCallbacks: Array<(value: any) => void> = []
private onRejectedCallbacks: Array<(reason: any) => void> = []
constructor(
executor: (
resolve: (value: any) => void,
reject: (reason: any) => void
) => void
) {
try {
executor(this.resolve.bind(this), this.reject.bind(this))
} catch (error) {
this.reject(error)
}
}
private resolve(value: any) {
if (value instanceof PfPromise) {
return value.then(this.resolve.bind(this), this.reject.bind(this))
}
setTimeout(() => {
if (this.status === 'pending') {
this.status = 'resolved'
this.value = value
this.onResolvedCallbacks.forEach((callback) => callback(value))
}
})
}
private reject(reason: any) {
setTimeout(() => {
if (this.status === 'pending') {
this.status = 'rejected'
this.value = reason
this.onRejectedCallbacks.forEach((callback) => callback(reason))
}
})
}
then(onFulfilled?: (value: any) => any, onRejected?: (reason: any) => any) {
return new PfPromise((resolve, reject) => {
const fulfilledCallback = () => {
try {
const result = onFulfilled ? onFulfilled(this.value) : this.value
resolvePromise(result, resolve, reject)
} catch (error) {
reject(error)
}
}
const rejectedCallback = () => {
try {
const result = onRejected ? onRejected(this.value) : this.value
resolvePromise(result, resolve, reject)
} catch (error) {
reject(error)
}
}
if (this.status === 'resolved') {
setTimeout(fulfilledCallback)
} else if (this.status === 'rejected') {
setTimeout(rejectedCallback)
} else {
this.onResolvedCallbacks.push(fulfilledCallback)
this.onRejectedCallbacks.push(rejectedCallback)
}
})
}
catch(onRejected: (reason: any) => any) {
return this.then(undefined, onRejected)
}
static all(promises: PfPromise[]) {
return new PfPromise((resolve, reject) => {
const results: any[] = []
let completed = 0
promises.forEach((promise, index) => {
promise
.then((data) => {
results[index] = data
completed++
if (completed === promises.length) {
resolve(results)
}
})
.catch(reject)
})
})
}
static race(promises: PfPromise[]) {
return new PfPromise((resolve, reject) => {
promises.forEach((promise) => {
promise.then(resolve).catch(reject)
})
})
}
static resolve(value: any) {
return new PfPromise((resolve) => resolve(value))
}
static reject(reason: any) {
return new PfPromise((_, reject) => reject(reason))
}
}
function resolvePromise(
x: any,
resolve: (value: any) => void,
reject: (reason: any) => void,
visited = new Set<any>()
) {
if (visited.has(x)) {
return reject(new TypeError('循环引用'))
}
if (x instanceof PfPromise) {
visited.add(x)
x.then(
(value) => resolvePromise(value, resolve, reject, visited),
(reason) => reject(reason)
)
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let then
try {
then = x.then
} catch (error) {
return reject(error)
}
if (typeof then === 'function') {
let called = false
try {
visited.add(x)
then.call(
x,
(y) => {
if (called) return
called = true
resolvePromise(y, resolve, reject, visited)
},
(r) => {
if (called) return
called = true
reject(r)
}
)
} catch (error) {
if (!called) {
reject(error)
}
}
} else {
resolve(x)
}
} else {
resolve(x)
}
}