前言
本节主要聊一聊 JavaScript 中异步编程的相关内容,在 JavaScript 的世界中,所有代码都是单线程执行的,无法同时执行多个任务。当需要执行一个可能耗时的操作,如文件读取、网络请求、用户的输入、定时器的回调等,这时需要使用异步编程的思想,确保不会阻塞程序的执行。
回调函数
假设现在有这样一个计算过程,对随机输入的一个数,需要做如下处理,
- 对该数进行除 2 取整处理
- 对于步骤 1 的结果进行平方处理
- 对于步骤 2 的结果进行取模 2 处理
如果我们使用回调函数要怎么写呢?
// 第一步:数字除以2
function divNumber(number, callback) {
number = number / 2
console.log('Step 1: division the number = ' + number);
callback(number);
}
// 第二步:计算数字的平方
function squareNumber(number, callback) {
const square = number * number;
console.log('Step 2: Squared the number = ' + square);
callback(square);
}
// 第三步:计算数字的取模2
function modSquare(square, callback) {
const mod = square % 2;
console.log('Step 3: Mod the square = ' + mod);
callback(mod);
}
// 执行计算过程
function doOperation(num) {
divNumber(num,(number) => {
squareNumber(number, (square) => {
modSquare(square, (result) => {
console.log('Result:', result);
});
});
});
}
doOperation(10)
// 输出
> Step 1: division the number = 5
> Step 2: Squared the number = 25
> Step 3: Mod the square = 1
> Result: 1
使用回调函数来解决这类执行过程依赖于上一步骤的结果的事件时,有时候会比较麻烦,尤其是当步骤的程序更复杂的情形时,就会陷入一种回调地狱
为了解决这类问题,ES6之后,JavaScript 引入了 Promise 这个新特性,使得 js 引擎可以主动执行异步任务
Promise
关于 Promise 的相关 API 可以参考MDN Promise
Promise 是 JavaScript 中用于处理异步操作的一种机制,它使得异步代码更易于编写和管理。一个 Promise 可以处于以下三种状态之一:
Pending(进行中)
: Promise 的初始状态,表示异步操作还在进行中。Fulfilled(已成功)
: 异步操作成功完成,并返回一个值,这时 Promise 进入 Fulfilled 状态。Rejected(已失败)
: 异步操作失败,返回一个错误信息,这时 Promise 进入 Rejected 状态。
new Promise
的构造函数接受一个函数作为参数,这个函数有两个参数,分别是 resolve
和 reject
。这个函数会在 Promise 被创建时立即执行。下面是基本的语法结构:
const myPromise = new Promise((resolve, reject) => {
// 异步操作
// 如果操作成功,调用 resolve 并传递成功的结果
// resolve(value);
// 如果操作失败,调用 reject 并传递失败的原因(通常是一个错误对象)
// reject(reason);
});
-
resolve
: 是一个函数,用于将 Promise 的状态从 Pending 转换为 Fulfilled,并传递一个值作为异步操作的成功结果。对应 then() 操作 -
reject
: 是一个函数,用于将 Promise 的状态从 Pending 转换为 Rejected,并传递一个原因(通常是一个错误对象)作为异步操作的失败原因。对应 cache() 操作
在 new Promise
的函数体内,你可以执行异步操作,当异步操作完成时,调用 resolve
或 reject
,从而改变 Promise 的状态。这就是为什么 Promise 被用于处理异步操作,使得异步代码更易于管理和组织。
在第一节中回调函数案例改为 promise 怎么写呢?
我们给最后的结果加一个判断,如果算出来的结果等于0,则使用 resolve 函数处理表示处理完成,如果结果不等于0,则使用 reject 函数处理。表示处理失败,并给第一步和第二步加上延时,模拟计算过程
// 第一步:数字除以2
function divNumber(number) {
return new Promise((resolve) => {
setTimeout(function() {
number = number / 2;
console.log('Step 1: division the number = ' + number);
resolve(number);
}, 2000)
});
}
// 第二步:计算数字的平方
function squareNumber(number) {
return new Promise((resolve) => {
setTimeout(function() {
const square = number * number;
console.log('Step 2: Squared the number = ' + square);
resolve(square);
}, 1000)
});
}
// 第三步:计算数字的取模2
function modSquare(square) {
return new Promise((resolve, reject) => {
const mod = square % 2;
console.log('Step 3: Mod the square = ' + mod);
if (mod == 0) {
resolve("result is zero")
} else {
reject("result is Non-zero")
}
resolve(mod);
});
}
// 执行计算过程
function doOperation(num) {
divNumber(num)
.then((number) => squareNumber(number))
.then((square) => modSquare(square))
.then((result) => {
console.log('Result:', result);
})
.catch((error) => {
console.error('Error:', error);
})
.finally(() => console.log('doOperation end'))
}
doOperation(10);
// 输出
Step 1: division the number = 5
Step 2: Squared the number = 25
Step 3: Mod the square = 1
Error: result is Non-zero
doOperation end
Promise 异步处理,如果是被 resolve 函数处理则会执行 then 块,then 接受的参数就是 resove 抛出去的值;如果是被 reject 函数处理,则会执行 cache 块,最后无能程序执行结果如何,都会执行 finally 块,结构有点像 java 中的 try cache finally
async
async 函数是使用async
关键字声明的函数。async 函数是 AsyncFunction 构造函数的实例,并且其中允许使用 await
关键字。async
和 await
关键字让我们可以用一种更简洁的方式写出基于 Promise
的异步行为,而无需刻意地链式调用 promise
。
再把之前的例子改些成 async 的方式实现
// 第一步:数字除以2
function divNumber(number) {
return new Promise((resolve) => {
setTimeout(function() {
number = number / 2;
console.log('Step 1: division the number = ' + number);
resolve(number);
}, 2000)
});
}
// 第二步:计算数字的平方
function squareNumber(number) {
return new Promise((resolve) => {
setTimeout(function() {
const square = number * number;
console.log('Step 2: Squared the number = ' + square);
resolve(square);
}, 1000)
});
}
// 第三步:计算数字的取模2
function modSquare(square) {
return new Promise((resolve) => {
const mod = square % 2;
console.log('Step 3: Mod the square = ' + mod);
resolve(mod);
});
}
// 执行计算过程
async function doOperation(num) {
try {
const number = await divNumber(num);
const square = await squareNumber(number);
const result = await modSquare(square);
console.log('Result:', result);
} catch (error) {
console.error('Error:', error);
}
}
doOperation(10);
// 输出
> Step 1: division the number = 5
> Step 2: Squared the number = 25
> Step 3: Mod the square = 1
> Result: 1
异步编程思想在现代的 web 开发中非常的使用,我们需要熟练的掌握相关功能的使用,尤其是 Promise 和 async 的理解