📜  JavaScript 中的异步代码是如何工作的?(1)

📅  最后修改于: 2023-12-03 15:16:09.406000             🧑  作者: Mango

JavaScript 中的异步代码是如何工作的?

JavaScript 是一门单线程语言,意味着它只能同时处理一个任务。但在实际开发中,经常需要进行诸如网络请求、文件读取等耗时操作,如果这些操作都是同步执行的话,那么整个程序就会被阻塞(即所有代码都要等待当前操作完成后再继续执行),造成比较糟糕的用户体验。

异步代码就是解决这一问题的方案,它可以让 JavaScript 进行非阻塞的处理。具体来说,异步代码可以在后台执行某个任务同时不影响程序继续执行,等到任务完成后再执行回调函数来处理结果。

回调函数

回调函数是异步编程中的一个核心概念,它通常用来处理异步操作完成后的结果,或者是在某个操作完成后执行一些操作。

以 Ajax 请求为例,如果请求成功,会调用相应的回调函数,处理返回的数据:

function ajaxFetch(url, callback) {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.onload = function() {
    if (xhr.status === 200) {
      callback(xhr.responseText);
    }
  };
  xhr.send();
}

ajaxFetch('https://jsonplaceholder.typicode.com/todos/1', function(data) {
  console.log(JSON.parse(data));
});

以上示例中,当调用 ajaxFetch 函数时会通过 XMLHttpRequest 请求对应的数据,如果请求成功就会执行传入的回调函数并把数据作为参数传入。这样就可以将数据返回到主流程中继续处理,而不必等到请求完成才能执行下面的代码。

定时器

另一个常见的异步操作是定时器,它可以在指定时间后执行一些操作。JavaScript 提供了两个与定时器相关的方法:setTimeoutsetInterval

setTimeout 可以在指定的时间后执行一次函数:

setTimeout(function() {
  console.log('执行了一次!');
}, 1000);

以上代码会在 1 秒后输出一条信息。setInterval 则可以按照指定的时间间隔重复执行函数:

let i = 1;
setInterval(function() {
  console.log('执行了第' + i + '次');
  i++;
}, 1000);

以上代码会每隔 1 秒输出一条信息。

Promise

使用回调函数可以实现异步编程,但当回调函数嵌套过多时就会变得非常难以维护。Promise 是一种用来解决这一问题的解决方案,它可以更好地管理异步操作。

Promise 由三种状态(pending、fulfilled、rejected)和相应的状态转换规则组成,它不同于回调函数通过返回值来处理异步操作结果的方式,而是通过在异步操作执行完毕后改变 Promise 的状态来处理异步操作结果。当 Promise 状态改变时会调用相应的回调函数,这些回调函数可以通过 then 方法来注册。例如:

const promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    if (Math.random() < 0.5) {
      resolve('操作成功!');
    } else {
      reject(new Error('操作失败!'));
    }
  }, 1000);
});

promise.then(function(result) {
  console.log(result);
}).catch(function(error) {
  console.log(error.message);
});

以上代码会创建一个新的 Promise 对象,使用定时器来模拟异步操作,在一个随机的时间后将状态改为 fulfilled 或者 rejected。当状态转换完成后,通过调用相应的回调函数来处理结果。

async 和 await

ES2017 中引入了 asyncawait 两个关键字,它们提供了一种更简单的方式来编写异步代码。

async 关键字可以用来声明一个函数是异步函数,该函数返回的是一个 Promise 对象。例如:

async function fetchData() {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
  const data = await response.json();
  return data;
}

fetchData().then(function(data) {
  console.log(data);
});

以上代码中,fetchData 函数使用了 async 关键字,因此它返回的是一个 Promise 对象,在函数内部使用 await 可以等待异步操作完成并返回结果。在这个例子中,函数会先请求数据并等待请求完成,然后通过 response.json() 方法来解析响应内容,并通过 return 返回处理后的数据。

await 关键字只能在 async 函数内部使用,它可以暂停函数的执行,等待异步操作完成并返回结果。当异步操作完成后,它会将其结果作为值返回给该表达式,然后重新启动函数的执行。

总结

异步代码是解决 JavaScript 单线程数据阻塞问题的解决方案之一,回调函数、定时器、Promise 以及 async 和 await 等是实现异步编程所必不可少的工具。理解这些概念并有能力正确应用它们对于一位优秀的前端工程师来说是非常重要的。