likes
comments
collection
share

异步的发展(上)

作者站长头像
站长
· 阅读数 22

异步(上)

前言

在现代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构造函数接受一个函数作为参数,该函数有两个参数:resolvereject。在异步操作完成后,可以调用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 方法会处理三种状态:fulfilledrejectedpending

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(尚未完成)时,不能立即执行回调函数。

  • onFulfilledonRejected 回调函数分别存入 this.onFulfilledCallbacksthis.onRejectedCallbacks 数组中。

  • 每个回调函数都包裹在一个新的函数中,并使用 setTimeout 确保它们在事件循环的下一轮执行。

  • 当 Promise 状态改变时,这些存储的回调函数将会被调用。

最后

在异步下篇我们会接着介绍异步实现的Generator 函数 和async/await

转载自:https://juejin.cn/post/7396930610624921638
评论
请登录