在 JavaScript 开发中,异步编程是一个常见的需求,尤其是在处理网络请求、文件操作或定时任务时。然而,传统的回调函数方式很容易导致代码嵌套过深,形成"回调地狱",使得代码难以阅读和维护。为了解决这一问题,Promise A+ 规范应运而生,并在 ES6 中得到了实现。
一、什么是 Promise
Promise 是一种用于处理异步操作的对象,它代表了一个可能还未完成的操作。Promise 有三个状态:
- Pending(挂起):初始状态,既不是成功,也不是失败。
- Fulfilled(完成):操作成功完成。
- Rejected(失败):操作失败。
Promise 的状态转换是单向的,一旦从 Pending 转变为 Fulfilled 或 Rejected,状态就固定下来,无法再改变。
javascript
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 的状态。以下是一个简单的例子:
javascript
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 可以轻松实现:
javascript
function delay(duration) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, duration);
});
}
delay(1000).then(() => {
console.log('finish');
});
在这个例子中,delay
函数返回一个 Promise 对象,该对象在指定的时间后通过 resolve
完成。
2. 加载图片
加载图片是一个常见的异步操作,我们可以使用 Promise 来处理图片的加载成功或失败:
html
<!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 来处理:
html
<!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,从而在实际开发中避免回调地狱的困扰。