Promise 基础:异步编程的救星
在 JavaScript 开发中,异步编程是一个常见的需求,尤其是在处理网络请求、文件操作或定时任务时。然而,传统的回调函数方式很容易导致代码嵌套过深,形成“回调地狱”,使得代码难以阅读和维护。为了解决这一问题,Promise A+ 规范应运而生,并在 ES6 中得到了实现。
一、什么是 Promise
Promise 是一种用于处理异步操作的对象,它代表了一个可能还未完成的操作。Promise 有三个状态:
- Pending(挂起):初始状态,既不是成功,也不是失败。
- Fulfilled(完成):操作成功完成。
- Rejected(失败):操作失败。
Promise 的状态转换是单向的,一旦从 Pending 转变为 Fulfilled 或 Rejected,状态就固定下来,无法再改变。
const pro1 = new Promise((resolve, reject) => {console.log('任务开始');resolve(1);reject(2); // 无效resolve(3); // 无效console.log('任务结束');
});console.log(pro1);pro1.then((data) => {console.log(data); // 1},(reason) => {console.log(reason);}
);
在这个例子中,resolve(1)
将 Promise 的状态从 Pending 转变为 Fulfilled,并传递了值 1
。后续的 reject(2)
和 resolve(3)
都是无效的,因为 Promise 的状态已经固定为 Fulfilled。
二、创建 Promise
Promise 的构造函数接受一个执行器函数,该函数会立即被调用,并且可以调用 resolve
或 reject
来改变 Promise 的状态。以下是一个简单的例子:
const pro = new Promise((resolve, reject) => {// 模拟异步操作setTimeout(() => {const success = true; // 假设这是异步操作的结果if (success) {resolve('操作成功');} else {reject('操作失败');}}, 1000);
});pro.then((data) => {console.log(data); // 操作成功},(reason) => {console.log(reason); // 操作失败}
);
在这个例子中,我们创建了一个 Promise 对象 pro
,并在 1 秒后通过 resolve
或 reject
改变了它的状态。then
方法用于指定任务完成或失败后的处理函数。
三、Promise 的实际应用
1. 延迟操作
假设我们需要一个函数来延迟一段指定的时间,然后执行某个操作。使用 Promise 可以轻松实现:
function delay(duration) {return new Promise((resolve) => {setTimeout(() => {resolve();}, duration);});
}delay(1000).then(() => {console.log('finish');
});
在这个例子中,delay
函数返回一个 Promise 对象,该对象在指定的时间后通过 resolve
完成。
2. 加载图片
加载图片是一个常见的异步操作,我们可以使用 Promise 来处理图片的加载成功或失败:
<!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><div class="container"></div><p class="label"></p><script>function createImage(imgUrl) {return new Promise((resolve, reject) => {const img = document.createElement('img');img.src = imgUrl;img.onload = () => {resolve(img);};img.onerror = (e) => {reject(e);};});}const url1 = 'myImage.jpg';createImage(url1).then((img) => {const p = document.querySelector('.label');p.innerHTML = `${img.width} * ${img.height}`;const div = document.querySelector('.container');div.appendChild(img);},(reason) => {console.log(reason);});</script>
</body>
</html>
在这个例子中,createImage
函数返回一个 Promise 对象,该对象在图片加载成功时通过 resolve
完成,并返回图片 DOM 元素;在图片加载失败时通过 reject
失败,并返回错误原因。
3. 加载远程数据
假设我们需要从远程服务器加载数据,可以使用 Promise 来处理:
<!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><select id="selProvince"></select><script>function getProvinces() {return fetch('https://study.duyiedu.com/api/citylist').then((resp) => resp.json()).then((resp) => resp.data).then((resp) =>resp.map((it) => ({ value: it.value, label: it.label })));}getProvinces().then((ps) => {const html = ps.map((p) => `<option value="${p.value}">${p.label}</option>`).join('');const selProvince = document.getElementById('selProvince');selProvince.innerHTML = html;},(reason) => {console.log(reason);});</script>
</body>
</html>
在这个例子中,getProvinces
函数返回一个 Promise 对象,该对象在成功加载数据时通过 resolve
完成,并返回省份数组;在加载失败时通过 reject
失败,并返回错误原因。
总结
Promise 是一种强大的工具,用于处理异步操作。它通过标准化的接口,使得异步代码更加清晰、简洁、统一。通过本文的介绍,希望你能够更好地理解和使用 Promise,从而在实际开发中避免回调地狱的困扰。