异步的发展(上)
异步(上)
前言
在现代JavaScript开发中,异步编程已经成为必不可少的技术之一。无论是在客户端还是服务器端开发中,异步编程都发挥着重要作用, 它使得我们可以在不阻塞主线程的情况下执行耗时操作。
JavaScript本身是一种单线程语言,即所有任务都在一个线程中执行,这意味着如果某个任务非常耗时,它会阻塞其他任务的执行,导致应用的响应变慢甚至无响应。为了解决这个问题,JavaScript提供了多种异步编程方式,如回调函数、Promise和async/await等。
回调函数
回调函数(callback)是最早期的一种异步编程方式,它通过将一个函数作为参数传递给另一个函数,在某个操作完成后再调用这个回调函数。下面是一个使用回调函数进行异步操作的例子:
function fetchData(callback) {
setTimeout(() => {
const data = { user: 'John Doe', age: 30 };
callback(null, data);
}, 2000);
}
function handleData(error, data) {
if (error) {
console.error('Error:', error);
} else {
console.log('Data:', data);
}
}
fetchData(handleData);
fetchData
函数模拟了一个异步操作,它接受一个回调函数callback
作为参数。在操作完成后(2秒后),回调函数被调用并传入获取的数据。
缺点
-
回调地狱:当多个异步操作需要依次执行时,回调函数的嵌套会变得非常深,代码变得难以阅读和维护。这种现象被称为“回调地狱”。
doSomething(function(result1) { doSomethingElse(result1, function(result2) { doAnotherThing(result2, function(result3) { doSomethingMore(result3, function(result4) { console.log('Final result:', result4); }); }); }); });
-
错误处理复杂:在回调函数中处理错误需要在每个回调中进行处理,这使得错误处理变得繁琐且容易遗漏。
asyncOperation(function(error, result) { if (error) { handleError(error); } else { asyncOperation2(result, function(error2, result2) { if (error2) { handleError(error2); } else { // ... } }); } });
-
无法返回值:由于回调函数是异步执行的,函数无法通过返回值直接返回异步操作的结果,而必须通过回调函数来传递结果,这在某些情况下会增加代码的复杂度。
promise
为了解决回调函数的这些缺点,ES6引入了Promise。Promise是一种用于处理异步操作的对象,它可以将异步操作的结果封装起来,并通过链式调用的方式处理异步操作的结果和错误。
下面是一个使用Promise进行异步操作的例子:
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { user: 'John Doe', age: 30 };
resolve(data);
}, 2000);
});
}
fetchData()
.then(data => {
console.log('Data:', data);
})
.catch(error => {
console.error('Error:', error);
});
fetchData
函数返回一个Promise对象。Promise
构造函数接受一个函数作为参数,该函数有两个参数:resolve
和reject
。在异步操作完成后,可以调用resolve
传递操作结果,或者调用reject
传递错误信息。
手写Promise
promise的三种状态
Pending(等待状态) :初始状态,即Promise对象刚被创建后的状态。
Fulfilled(成功状态) :操作成功完成,Promise 得到了一个值。
Rejected(失败状态) :操作失败,Promise 得到了一个拒因。
promise类
promise是一个类,下面是其基本框架和定义了的实例属性
class MyPromise {
// 该构造函数传入executor执行函数
constructor(executor) {
// promise 状态值
this.state = pending;
// 成功的值
this.value = underfined;
// 失败的原因
this.reason = underfined;
// 存入成功的回调函数
this.onFulfilledCallbacks = [];
// 存入失败的回调函数
this.onRejectedCallbacks = [];
// resolve函数
const resolve = () => {};
// reject函数
const reject = () => {};
// executor立即执行函数传入resolve函数,和reject函数
executor(resolve, reject);
}
}
resolve、reject
resolve函数和reject函数执行逻辑如下。
// resolve函数
const resolve = (value) => {
if (this.state === pending) {
//状态改变为fulfilled
this.state = fulfilled;
// 存储传入成功的值
this.value = value;
//执行成功的回调函数
this.onFulfilledCallbacks.forEach((fn) => fn(value));
}
};
// reject函数
const reject = (reason) => {
if (this.state === pending) {
//状态改变为rejected
this.state = rejected;
//存储传入的失败的原因
this.reason = reason;
// 执行失败的回调函数
this.onRejectedCallbacks.forEach((fn) => fn(reason));
}
};
then方法
在 Promise 中,then
方法用于注册处理异步操作成功或失败的回调函数。其基本语法为:
-
onFulfilled
是一个可选的回调函数,用于处理操作成功时的结果。 -
onRejected
是一个可选的回调函数,用于处理操作失败时的原因
// 两个默认的参数
// onFulfilled、onRejected
then(onFulfilled, onRejected) {
// 判断这个两个参数是不是函数
onFulfilled =
typeof onFulfilled === "function" ? onFulfilled : (value) => value;
onRejected =
typeof onRejected === "function" ? onRejected : (reason) => reason;
}
then
方法需要并返回一个新的 Promise
,以支持链式调用,其中then
方法会处理三种状态:fulfilled
、rejected
和 pending
。
return new MyPromise((resolve, reject) => {
if (this.state === "fulfilled") {
}
if (this.state === "rejected") {
}
if (this.state === "pending") {
}
});
状态为 fulfilled 时的处理
if (this.state === "fulfilled") {
setTimeout(() => {
try {
const res = onFulfilled(this.value);
resolve(res);
} catch (error) {
reject(error);
}
});
}
解释:
-
当 Promise 的状态已经变为
fulfilled
时,立即执行onFulfilled
回调函数。 -
使用
setTimeout
模拟异步操作,使回调函数在事件循环的下一轮执行,这是为了保证then
方法的行为符合异步规范。
状态为 rejected 时的处理
if (this.state === "rejected") {
setTimeout(() => {
try {
const res = onRejected(this.reason);
resolve(res);
} catch (error) {
reject(error);
}
});
}
解释:
-
当 Promise 的状态已经变为
rejected
时,立即执行onRejected
回调函数。 -
同样使用
setTimeout
模拟异步操作。
状态为 pending 时的处理
if (this.state === "pending") {
this.onFulfilledCallbacks.push((value) => {
setTimeout(() => {
onFulfilled(value);
});
});
this.onRejectedCallbacks.push((reason) => {
setTimeout(() => {
onRejected(reason);
});
});
}
解释:
-
当 Promise 的状态为
pending
(尚未完成)时,不能立即执行回调函数。 -
将
onFulfilled
和onRejected
回调函数分别存入this.onFulfilledCallbacks
和this.onRejectedCallbacks
数组中。 -
每个回调函数都包裹在一个新的函数中,并使用
setTimeout
确保它们在事件循环的下一轮执行。 -
当 Promise 状态改变时,这些存储的回调函数将会被调用。
最后
在异步下篇我们会接着介绍异步实现的Generator
函数 和async/await
。
转载自:https://juejin.cn/post/7396930610624921638