小马的世界

JS学习日记【11】收尾:异步通信 Storage/FetchAPI/Promise/Web worker

2023-11-17 · 10 min read

Storage 对象

Storage 对象是 JavaScript 中用于存储数据的一个重要工具。它有两种形式:localStoragesessionStorage。下面通过一个具体的例子来解释这两种形式。

localStorage

  • 描述: localStorage 用于长期存储数据,直到手动清除。
  • 生命周期: 在多个浏览器会话之间持久。
  • 存储限制: 通常为 5MB。

示例

假设我们要在用户的浏览器中保存他们的用户名。

// 保存用户名
localStorage.setItem('username', 'user123');

// 获取用户名
let username = localStorage.getItem('username');
console.log(username); // 输出: user123

// 移除用户名
localStorage.removeItem('username');

sessionStorage

  • 描述: sessionStorage 用于临时存储数据,在浏览器窗口或标签页关闭时清除。
  • 生命周期: 仅在当前浏览器窗口或标签页中有效。
  • 存储限制: 通常也为 5MB。

示例

假设我们要在用户浏览某个页面时临时存储一个会话ID。

// 保存会话ID
sessionStorage.setItem('sessionId', 'abc123');

// 获取会话ID
let sessionId = sessionStorage.getItem('sessionId');
console.log(sessionId); // 输出: abc123

// 移除会话ID
sessionStorage.removeItem('sessionId');

注意事项

  • 对于敏感数据,不建议使用 localStoragesessionStorage,因为它们在客户端存储,可能存在安全风险。
  • 存储的数据都是字符串类型,如果需要存储对象,可以使用 JSON.stringify 来转换对象为字符串,获取时再用 JSON.parse 还原。

异步通信和 Fetch API

异步通信是 Web 开发中的一个重要概念,它允许 Web 应用在不干扰用户界面的情况下与服务器进行数据交换。Fetch API 是实现异步通信的现代方法之一。

什么是异步通信(Asynchronous Communication)

在理解异步通信之前,我们先来看一下同步通信。

  • 同步通信: 在同步操作中,任务按顺序执行,每个任务的完成是下一个任务开始的前提。这意味着如果某个任务耗时较长,后续的任务都必须等待。

  • 异步通信: 异步操作允许在等待耗时任务(例如网络请求)的同时继续执行其他任务。这意味着应用可以在没有明显延迟的情况下响应用户交互,同时进行数据加载等操作。

Fetch API

Fetch API 提供了一个强大且灵活的方法来发出异步 HTTP 请求。它是 XMLHttpRequest 的现代替代品,提供了更简洁、更强大的功能。

基本使用

以下是 Fetch API 的一个基本示例:

fetch('https://api.example.com/data')
  .then(response => {
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }
    return response.json(); // 解析 JSON 数据
  })
  .then(data => {
    console.log(data); // 处理数据
  })
  .catch(error => {
    console.error('There has been a problem with your fetch operation:', error);
  });

在这个示例中:

  1. fetch 函数调用一个 URL(在这个例子中是 'https://api.example.com/data')。
  2. 返回一个 Promise,它是异步操作的一种表示方式。
  3. 使用 .then() 方法来定义当 Promise 完成时应该发生的事情。第一个 .then() 调用是用来检查响应并解析 JSON 数据。
  4. 第二个 .then() 调用处理解析后的数据。
  5. .catch() 方法用于捕获任何在请求过程中发生的错误。

高级特性

  • 发送 POST 请求:
  fetch('https://api.example.com/data', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ key: 'value' }) // 发送 JSON 数据
  });
  • 异步/等待(Async/Await): 这是一种更现代的处理异步操作的方式。
  async function fetchData() {
    try {
      const response = await fetch('https://api.example.com/data');
      const data = await response.json();
      console.log(data);
    } catch (error) {
      console.error('Error:', error);
    }
  }

  fetchData();

总结

异步通信允许 Web 应用在后台执行耗时任务(如数据请求),同时不阻塞用户界面。Fetch API 是实现这种通信的强大工具,它通过 Promise 提供了一种优雅的方式来处理异步 HTTP 请求。通过 Fetch API,开发者可以轻松实现数据的加载、提交等操作,提高应用的性能和用户体验。

Promise 对象

Promise 对象是 JavaScript 中处理异步操作的关键概念。在深入了解 Promise 之前,我们需要理解异步编程在 JavaScript 中的重要性。

异步编程

JavaScript 是一种单线程语言,意味着它一次只能执行一个任务。为了不阻塞代码执行(例如等待网络请求、读取文件等耗时操作),JavaScript 使用异步编程模式。这就是 Promise 发挥作用的地方。

什么是 Promise

Promise 是一个表示未来将会完成的操作的对象。它可以处于以下三种状态之一:

  1. Pending(进行中): 初始状态,既不是成功,也不是失败状态。
  2. Fulfilled(已成功): 操作成功完成。
  3. Rejected(已失败): 操作失败。

Promise 提供了一种优雅的方式来处理异步操作的成功或失败,允许你编写更简洁、易于理解的代码,特别是在处理多个异步操作时。

创建一个 Promise

Promise 是通过 new Promise() 构造函数创建的,它接受一个函数作为参数,该函数有两个参数:resolvereject。这两个参数也是函数,用于改变 Promise 的状态。

let promise = new Promise(function(resolve, reject) {
  // 异步操作的代码...

  if (/* 操作成功 */) {
    resolve(value); // 将 Promise 的状态改为 Fulfilled
  } else {
    reject(error); // 将 Promise 的状态改为 Rejected
  }
});

使用 Promise

一旦 Promise 被创建,你可以使用 .then().catch().finally() 方法来处理成功、失败和完成(无论成功或失败)的情况。

  • .then() 接受两个函数作为参数,第一个处理成功的情况,第二个(可选的)处理失败的情况。
  • .catch() 用于捕获失败的情况。
  • .finally() 是无论成功还是失败都会执行的。
promise
  .then(
    result => { /* 处理成功的结果 */ },
    error => { /* 处理错误 */ }
  )
  .catch(error => { /* 只处理错误 */ })
  .finally(() => { /* 总是会执行的代码 */ });

链式调用

Promise 的一个强大特性是支持链式调用。你可以在一个 Promise.then() 方法中返回另一个 Promise,然后继续使用 .then() 处理后续操作。

fetch('https://api.example.com/data') // Fetch 返回一个 Promise
  .then(response => response.json()) // 第一个 .then 返回一个新的 Promise
  .then(data => { /* 处理数据 */ })
  .catch(error => { /* 处理错误 */ });

Promise.all

当你需要同时处理多个 Promise 并等待它们全部完成时,可以使用 Promise.all。这个方法接收一个 Promise 数组,并返回一个新的 Promise

Promise.all([promise1, promise2, promise3])
  .then(results => {
    // 当所有 promises 都完成时执行
    // results 是一个数组,包含每个 promise 的结果
  })
  .catch(error => {
    // 如果任何一个 promise 失败
  });

总结

Promise 是 JavaScript 中处理异步操作的强大工具。它提供了一种清晰的方式来处理异步操作的成功或失败,并支持链式调用和组合多个异步操作。这使得编写异步代码变得更加简洁和易于管理。

Web Workers

当然可以。Web Workers 是一个强大的 Web API,它允许在 JavaScript 的主执行线程之外运行脚本,实现真正的多线程编程。由于 JavaScript 通常是单线程的,长时间运行的或复杂的计算任务可能会阻塞用户界面。Web Workers 通过允许这些任务在后台线程中运行来解决这个问题。

基本概念

  • 多线程: Web Workers 允许你在单独的线程中运行代码,这意味着你可以执行长时间运行的任务,而不会阻塞浏览器的主线程。
  • 独立运行: Worker 线程和主线程完全独立,它们不共享作用域或任何全局数据。这意味着它们不能直接访问 DOM 或其他主线程特有的对象。

创建和使用 Web Worker

  1. 创建 Worker 文件: 首先,需要创建一个 JavaScript 文件,其中包含将在 Worker 线程中执行的代码。
   // worker.js
   self.addEventListener('message', function(e) {
     var data = e.data;
     // 执行一些操作...
     var result = processData(data);
     self.postMessage(result);
   });
  1. 实例化 Worker: 在主 JavaScript 文件中,你可以创建一个 Worker 实例,并指向上述 JavaScript 文件。
   // 主线程
   var myWorker = new Worker('worker.js');

   myWorker.postMessage(data); // 发送数据到 Worker

   myWorker.addEventListener('message', function(e) {
     var result = e.data;
     // 使用 Worker 计算的结果
   });

通信

  • 主线程与 Worker 通信: 使用 postMessage() 方法发送消息,使用 onmessageaddEventListener 监听消息。
  • 数据传输: 数据是通过结构化克隆算法复制的,这意味着发送给 Worker 的对象是其副本,而不是原始对象。

限制和注意事项

  • 无 DOM 访问: Workers 无法访问 DOM。它们不能更新界面元素。
  • 限制的全局对象: Workers 只能访问部分全局对象和函数,如 navigatorsetTimeout
  • 内存和性能考虑: 虽然 Workers 可以提高性能,但过多的 Workers 可能会增加内存使用并降低性能。

应用场景

Web Workers 特别适用于那些需要执行密集型计算或长时间运行的任务的应用,例如:

  • 大数据处理
  • 图像或视频处理
  • 复杂数学计算
  • 后台数据处理或预取

总结

Web Workers 为 Web 应用提供了真正的多线程能力,允许在后台线程中执行 JavaScript,从而提高应用的响应性和性能。通过将复杂的计算任务从主线程中分离出来,Web Workers 确保主线程(负责 UI 和用户交互)保持流畅和响应。尽管有一些限制和考虑因素,但它们是现代 Web 应用中不可或缺的工具。