在JavaScript中,Promise 是一个表示异步操作最终完成(或失败)及其结果的对象。Promise 提供了一种更优雅的方式来处理异步操作,避免了回调地狱(callback hell)的问题。
Promise 的基本概念
状态
Promise 有三种状态:
- Pending(进行中) :初始状态,既不是成功,也不是失败。
- Fulfilled(已成功) :操作成功完成。
- Rejected(已失败) :操作失败。
方法
then():用于处理异步操作成功和失败的回调函数。catch():用于捕获异步操作中的错误。finally():无论异步操作成功或失败,都会执行的回调函数。
创建 Promise
示例
const myPromise = new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
const success = true; // 假设这是异步操作的结果
if (success) {
resolve('操作成功');
} else {
reject('操作失败');
}
}, 1000);
});
myPromise.then((result) => {
console.log(result); // 输出:操作成功
}).catch((error) => {
console.error(error); // 输出:操作失败
}).finally(() => {
console.log('操作完成');
});
Promise 的链式调用
示例
function fetchData(url) {
return new Promise((resolve, reject) => {
// 模拟HTTP请求
setTimeout(() => {
const data = { id: 1, name: 'Kimi' };
resolve(data);
}, 1000);
});
}
function process_data(data) {
return new Promise((resolve, reject) => {
// 模拟数据处理
setTimeout(() => {
const processedData = { ...data, processed: true };
resolve(processedData);
}, 1000);
});
}
fetchData('https://api.example.com/data')
.then(data => {
console.log('获取数据成功:', data);
return process_data(data);
})
.then(processedData => {
console.log('处理数据成功:', processedData);
})
.catch(error => {
console.error('发生错误:', error);
});
方法
Promise.all
Promise.all 用于同时处理多个 Promise 对象。当所有 Promise 都成功时,Promise.all 返回一个新的 Promise。如果一个 Promise 失败,Promise.all 会立即返回一个失败的 Promise。
示例
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values); // 输出:[3, 42, 'foo']
});
Promise.allSettled()
Promise.allSettled() 是 JavaScript 中的一个 Promise 方法,它接收一个 Promise 数组,并在所有 Promise 对象都已解决或拒绝后返回一个 Promise 对象,该对象解析为一个数组,每个元素对应相应的 Promise 对象的结果。
与 Promise.all() 不同的是,Promise.allSettled() 不会在任何 Promise 被拒绝时中止,并且总是等待所有 Promise 对象都已解决或拒绝后才返回结果。返回的数组中的每个元素都是一个对象,包含以下属性之一:
- status:表示 Promise 的状态,可能的值为 “fulfilled”(已解决)或 “rejected”(已拒绝)。
- value:如果 Promise 已解决,则为解决值;如果 Promise 已拒绝,则为拒绝原因。
举例:
Promise.allSettled([
Promise.resolve(33),
new Promise((resolve) => setTimeout(() => resolve(66), 0)),
99,
Promise.reject(new Error("一个错误")),
]).then((values) => console.log(values));
// [
// { status: 'fulfilled', value: 33 },
// { status: 'fulfilled', value: 66 },
// { status: 'fulfilled', value: 99 },
// { status: 'rejected', reason: Error: 一个错误 }
// ]
Promise.any()
执行所有 promise,返回最先返回的成功结果;全部失败才判定为失败
const pErr = new Promise((resolve, reject) => {
reject("总是失败");
});
const pSlow = new Promise((resolve, reject) => {
setTimeout(resolve, 500, "最终完成");
});
const pFast = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "很快完成");
});
Promise.any([pErr, pSlow, pFast]).then((value) => {
console.log(value);
// pFast 第一个兑现
});
// 输出:很快完成
Promise.race
Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
示例
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'one');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 200, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value); // 输出:'one'
});
Promise.try()
const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now
上面代码中,函数f是同步的,但是用 Promise 包装了以后,就变成异步执行了。
那么有没有一种方法,让同步函数同步执行,异步函数异步执行,并且让它们具有统一的 API 呢?回答是可以的。
const f = () => console.log('now');
(
() => new Promise(
resolve => resolve(f())
)
)();
console.log('next');
// now
// next
鉴于这是一个很常见的需求,所以现在有一个提案,提供Promise.try方法替代上面的写法。
const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next
Promise.withResolvers()
在此之前我们创建 Promise 实例是这样的:
const promiseInstance = new Promise<void>((resolve,reject)=>{
// ...
})
现在,我们也能够通过 Promise.withResolvers 来创建实例:
const { promise, resolve, reject } = Promise.withResolvers<void>()
但它并不是为了替代原有创建 Promise 实例的方式,引入它实际上是为了 打破 构造器模式创建 Promise 实例时,resolve 与 reject 方法只能在构造器回调函数中使用的 限制。
在它出现之前,我们也可以通过如下代码实现相同的效果:
let resolve, reject
const promise = new Promise((res, rej) => {
resolve = res
reject = rej
})
Promise 的错误处理
示例
function fetchData(url) {
return new Promise((resolve, reject) => {
// 模拟HTTP请求
setTimeout(() => {
const success = false; // 假设请求失败
if (success) {
resolve('数据获取成功');
} else {
reject('数据获取失败');
}
}, 1000);
});
}
fetchData('https://api.example.com/data')
.then(data => {
console.log('获取数据成功:', data);
})
.catch(error => {
console.error('发生错误:', error);
});
async/await
async/await 是基于 Promise 的语法糖,使异步代码看起来更像同步代码,提高了代码的可读性和易用性。
示例
async function fetchData(url) {
try {
const response = await fetch(url);
const data = await response.json();
console.log('获取数据成功:', data);
} catch (error) {
console.error('发生错误:', error);
}
}
fetchData('https://api.example.com/data');
JS中的其它异步处理机制
- 回调函数(Callbacks)
回调函数是最基本的异步处理机制。通过将一个函数作为参数传递给另一个函数,并在异步操作完成后调用该回调函数,可以实现异步操作。
function fetchData(callback) {
setTimeout(() => {
const data = { id: 1, name: 'Kimi' };
callback(data);
}, 1000);
}
fetchData((data) => {
console.log('数据获取成功:', data);
});
- 事件监听(Event Listeners)
事件监听机制允许你为特定的事件注册回调函数。当事件发生时,注册的回调函数会被自动调用。这在DOM操作中非常常见。
document.getElementById('myButton').addEventListener('click', (event) => {
console.log('按钮被点击');
});
- 发布/订阅模式(Publish/Subscribe)
发布/订阅模式允许对象订阅特定的事件,并在事件发生时接收通知。这种模式在大型应用程序中非常有用,可以实现模块之间的解耦。
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(listener => {
listener(...args);
});
}
}
}
const emitter = new EventEmitter();
emitter.on('data', (data) => {
console.log('接收到数据:', data);
});
emitter.emit('data', { id: 1, name: 'hello' });
- Generator 函数
Generator函数是一种可以暂停和恢复执行的函数,可以用于实现异步操作的控制流。通过yield关键字,可以在函数内部暂停执行,并在外部恢复执行。
function* fetchDataGenerator(url) {
try {
const response = yield fetch(url);
const data = yield response.json();
console.log('数据获取成功:', data);
} catch (error) {
console.error('发生错误:', error);
}
}
const generator = fetchDataGenerator('https://api.example.com/data');
generator.next().value.then(response => {
return generator.next(response).value;
}).then(data => {
generator.next(data);
});
- Web Workers
Web Workers允许你在后台线程中运行JavaScript代码,从而实现多线程处理。这在处理大量数据或执行耗时操作时非常有用,可以避免阻塞主线程。
// main.js
const worker = new Worker('worker.js');
worker.onmessage = (event) => {
console.log('从Worker接收到数据:', event.data);
};
worker.postMessage('开始处理数据');
// worker.js
self.onmessage = (event) => {
console.log('接收到消息:', event.data);
// 处理数据
const result = { id: 1, name: 'Hello' };
self.postMessage(result);
};
- Observables(RxJS)
Observables是Reactive Programming(响应式编程)中的一个概念,用于处理异步数据流。RxJS库提供了丰富的操作符,可以方便地处理复杂的异步操作。
import { fromEvent } from 'rxjs';
import { debounceTime, map } from 'rxjs/operators';
const inputElement = document.getElementById('input');
fromEvent(inputElement, 'input')
.pipe(
debounceTime(300),
map(event => event.target.value)
)
.subscribe(value => {
console.log('输入值:', value);
});