一、异步
1.异步的基本概念
在我们了解promise之前,我们首先要了解 异步 的概念。
异步(Asynchronous):
- 是一种编程模式,指的是程序执行过程中,某些操作不需要等待其他操作完成就可以继续进行。在异步编程中,任务的执行顺序不是按照代码的书写顺序来确定的,而是由程序运行时环境来决定。
我们来举个例子:
function a() {
setTimeout(function () {
console.log('喜羊羊吃火锅');
}, 1000)
}
a()
function b() {
console.log('喜羊羊在刷抖音');
}
b()
在这个例子中,很明显,a是一个定时器函数,耗时1秒,b是即刻打印。 那么大家可以根据异步的概念来猜一下,a 和 b 是谁先打印呢?
没错!相信大家都有答案了,实际上是 b 先打印,1秒后才打印 a,这也就是异步的处理了。
2.异步的优点
因此我们知道,异步编程通常用于处理可能耗时的任务,比如网络请求、文件读写、定时器等。在这些情况下,如果程序等待这些任务完成,会导致阻塞,降低程序的性能和响应速度。通过异步编程,程序可以在执行这些任务的同时继续执行后续代码,提高了程序的效率。
异步编程通常用于处理可能耗时的任务,比如网络请求、文件读写、定时器等。在这些情况下,如果程序等待这些任务完成,会导致阻塞,降低程序的性能和响应速度。通过异步编程,程序可以在执行这些任务的同时继续执行后续代码,提高了程序的效率。这就是异步的优点。
3.异步的弊端
但是程序的效率异步确实给我们提高了,但是有的时候需求要我们必须先执行第一项,再去执行其他的,这样要怎么处理呢?
一般而言,我们用回调函数来处理:
function a(cb) {
setTimeout(() => {
console.log('木木');
cb()
}, 1000)
}
function b() {
setTimeout(() => {
console.log('清华大学');
}, 500)
}
a(b)
这个例子中,在原始状态下,异步编程之下应该实现打印快的那个,也就是先打印清华大学
,再打印木木
对不对?
现在假设我们必须要先拿到用户姓名,然后才能拿到用户学校,我们采用回调函数的方法,直接在函数a中调用b,可以满足我们的想法。
那这里就有问题了,使用回调函数处理多个嵌套的异步操作时,代码可能会变得非常混乱,形成所谓的“回调地狱”。这使得代码难以理解和维护,可读性降低。
回调地狱(Callback Hell):
- 问题: 在多个嵌套的回调函数中编写代码,使得代码难以阅读和维护。
1> 回调地狱(Callback Hell)
想想看,假设我们有三个异步任务:task1
、task2
、task3
,它们需要按顺序执行。在回调函数中,代码可能会看起来像这样:
task1((result1) => {
console.log(result1);
task2((result2) => {
console.log(result2);
task3((result3) => {
console.log(result3);
// 后续操作...
});
});
});
这还只是三个任务,代码已经有点复杂了,那如果是十个、百个...甚至一千个呢?
2> 并发控制
另外,使用传统的回调函数,我们可能会陷入并发控制的问题。比如,我们希望等待task1
和task2
都完成后执行task3
:
task1((result1) => {
console.log(result1);
task2((result2) => {
console.log(result2);
// 等待task1和task2完成后执行task3
task3((result3) => {
console.log(result3);
// 后续操作...
});
});
});
3> 错误处理
错误处理在回调中可能会变得混乱,错误可能被传递到回调链的某个地方,难以捕获和处理。
task1((result1, error1) => {
if (error1) {
console.error(error1);
} else {
console.log(result1);
task2((result2, error2) => {
if (error2) {
console.error(error2);
} else {
console.log(result2);
task3((result3, error3) => {
if (error3) {
console.error(error3);
} else {
console.log(result3);
// 后续操作...
}
});
}
});
}
});
这就是异步编程的弊端了,当我们要处理多个复杂任务时,异步会让代码变得难以理解且可读性低,那么我们可以用什么方法解决回调地狱,让我们写的代码既简单又容易理解呢?
接下来!就要有请我们今天的主角——Promise出场了!
二、Promise
1.Promise的基本语法
1> 什么是Promise?
Promise 是 JavaScript 中用于处理异步操作的对象。它是一种为了更清晰和更结构化地处理异步代码而引入的编程模型。Promise 对象代表一个尚未完成并且可能在未来完成的操作,可以是一个异步操作,也可以是一个同步操作。
2> Promise的三种状态
- Pending(进行中): 初始状态,表示操作尚未完成,处于进行中。
- Fulfilled(已完成): 表示操作成功完成。
- Rejected(已失败): 表示操作失败。
3> Promise的基本用法
创建一个 Promise 对象的基本语法如下:
const promise = new Promise((resolve, reject) => {
// 异步操作,比如网络请求、文件读写等
// 如果操作成功,调用 resolve,并传递结果
// 如果操作失败,调用 reject,并传递错误信息
});
4> Promise 的主要特点
-
链式调用: 可以使用
.then()
方法处理异步操作的结果,从而形成链式调用,使代码更加清晰。promise.then( (result) => { // 处理操作成功的情况 console.log(result); }, (error) => { // 处理操作失败的情况 console.error(error); } );
-
异步错误处理: 使用
.catch()
方法捕获 Promise 链中的任何错误。promise .then((result) => { // 处理操作成功的情况 console.log(result); }) .catch((error) => { // 处理操作失败的情况 console.error(error); });
-
Promise.all(): 处理多个 Promise 对象,等待所有 Promise 完成。
const promises = [promise1, promise2, promise3]; Promise.all(promises) .then((results) => { // 处理所有 Promise 成功完成的情况 console.log(results); }) .catch((error) => { // 处理任何一个 Promise 失败的情况 console.error(error); });
Promise 的引入旨在解决回调地狱(Callback Hell) 和提高异步代码的可读性。后续的 ECMAScript 版本引入了 async/await 语法,进一步简化了异步代码的书写。
5> 举例
这里举个很好理解的例子:
function breakfast() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('吃早饭');
resolve('吃完早饭啦!')
}, 2000)
})
}
function lunch() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('吃中饭');
resolve('吃完中饭啦!')
}, 1000)
})
}
function dinner() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('吃晚饭');
resolve('吃完晚饭啦!')
}, 500)
})
}
breakfast()
.then((res) => {
console.log(res);
return lunch()
})
.then(res2 => {
console.log(res2);
return dinner()
})
这里的一段代码使用了 Promise 来模拟一天中的吃饭过程,并展示了如何通过 Promise 的链式调用处理异步操作。
-
在这个链式调用中,
breakfast()
返回的 Promise 被解析为 Fulfilled 后,.then((res) => { ... })
中的回调函数被执行,打印出'吃完早饭啦!'
,然后返回lunch()
函数的 Promise。 -
接着,当
lunch()
完成后,.then(res2 => { ... })
中的回调函数被执行,打印出'吃完中饭啦!'
,并返回dinner()
函数的 Promise。 -
最后,当
dinner()
完成后,整个 Promise 链结束。
这种链式调用的方式使得异步操作的代码看起来更加清晰和顺序,避免了回调地狱,提高了可读性。整体执行流程是按照顺序执行三顿饭的操作,每一顿饭的完成都触发了下一顿饭的开始。
2. Promise的状态转换
Promise 对象有三个状态:Pending(进行中)、Fulfilled(已完成)、Rejected(已失败)。一个 Promise 对象的状态可以从 Pending 转换为 Fulfilled 或 Rejected,但一旦状态被转换,就不可再次修改。以下是 Promise 的状态转换过程:
1> Pending(进行中)
Promise 对象初始时处于 Pending 状态,表示操作尚未完成。
const promise = new Promise((resolve, reject) => {
// 异步操作
});
2> Fulfilled(已完成)
如果异步操作成功,调用 resolve(result)
将 Promise 的状态从 Pending 转换为 Fulfilled。
const promise = new Promise((resolve, reject) => {
// 异步操作成功
resolve("操作成功");
});
3> Rejected(已失败)
如果异步操作失败,调用 reject(error)
将 Promise 的状态从 Pending 转换为 Rejected。
const promise = new Promise((resolve, reject) => {
// 异步操作失败
reject("操作失败");
});
4> 示例:
const examplePromise = new Promise((resolve, reject) => {
const isSuccess = true; // 模拟异步操作成功或失败
if (isSuccess) {
resolve("操作成功");
} else {
reject("操作失败");
}
});
// 使用 .then() 处理 Fulfilled 状态
examplePromise.then(
(result) => {
console.log("Fulfilled:", result);
},
(error) => {
console.error("Rejected:", error);
}
);
在这个示例中,examplePromise
创建时处于 Pending 状态,根据 isSuccess
的值,可能会转换为 Fulfilled 或 Rejected 状态。根据状态的不同,分别执行了对应的回调函数。
Promise 对象的状态转换是一次性的,一旦状态转换完成,就无法再次修改。这种特性使得 Promise 更容易管理异步操作的状态和结果。
三、总结
在前面的讨论中,我们了解了关于Promise和异步编程的一系列解释和示例。以下是主要内容的总结:
-
异步编程:
- 异步编程是一种编程模式,允许程序在执行耗时的操作时继续执行后续代码,而不必等待操作完成。
- JavaScript中的异步编程常用于处理网络请求、文件读写、定时器等需要等待的操作。
-
回调函数:
- 回调函数是一种处理异步操作的传统方式,通过将函数作为参数传递给另一个函数,在异步操作完成时调用该函数。
-
Promise:
- Promise是JavaScript中处理异步操作的对象,提供了更清晰、更结构化的方式来处理异步代码。
- Promise对象有三个状态:Pending(进行中)、Fulfilled(已完成)、Rejected(已失败)。
- 创建Promise对象时,通过Promise构造函数传递一个执行器函数,该函数接收两个参数resolve和reject,分别用于操作成功和失败时的处理。
-
Promise的基本用法:
- 使用
.then()
处理Promise对象的结果,形成链式调用,每个.then()
处理一个异步操作的结果。 - 使用
.catch()
捕获Promise链中的任何错误。 - 使用
Promise.all()
处理多个Promise对象,等待所有Promise完成。
- 使用
-
异步编程的挑战:
- 异步编程带来了一些挑战,如回调地狱、并发控制、错误处理等。
- 这些挑战可以通过Promise和async/await等现代异步编程工具来更优雅地解决。
-
Promise的状态转换:
- Promise对象的状态可以从Pending转换为Fulfilled或Rejected。
- 一旦状态被转换,就不可再次修改,保证了Promise的不可变性。
-
示例:
- 提供了一个以吃饭过程为例的代码,展示了如何使用Promise来处理异步操作,形成Promise链,实现清晰的异步代码流程。
总体而言,这些内容涵盖了JavaScript中异步编程的基本概念、Promise的基本用法和一些异步编程的挑战以及解决方案,在下一篇我们将会聊到Promise的错误处理、Promise的实际应用还有Promise的高级用法等等内容。
结语
那么我们今天的内容就结束啦,欢迎各路大神在评论区讨论~~
点赞收藏不迷路,咱们下期再见ヾ( ̄▽ ̄)ByeBye~