Promise处理异步操作

在 Promise 成为 JavaScript 标准(ES6)之前,处理异步操作主要依赖于回调函数 (Callback Functions)。异步函数会接受一个函数作为参数,这个函数会在异步操作完成时被调用。

场景对比:实现一个依赖前一个异步操作结果的异步序列

假设我们需要实现一个流程:

  1. 等待 1 秒。
  2. 获取一个初始值。
  3. 将这个值变成大写(这个操作本身也可以是异步的)。
  4. 等待 1 秒。
  5. 将处理后的值加上一个后缀。
  6. 最后打印出最终结果,或捕获过程中发生的任何错误。

场景 1:不使用 Promise (只使用嵌套回调 的异步- 回调地狱)

在这种模式下,每个异步操作完成后,你需要调用下一个异步操作,并将下一个步骤的代码写在前一个回调函数内部。当异步操作增多并互相依赖时,代码会向右侧层层嵌套,形成“回调地狱”(Callback Hell) 或“末日金字塔”(Pyramid of Doom)。

// 模拟一个基于回调的异步函数
function delayedUpperCaseCallback(str, delay, callback) {
  setTimeout(() => {
    if (typeof str !== 'string') {
      // 模拟错误
      callback(new Error("输入必须是字符串"), null);
    } else {
      const result = str.toUpperCase();
      console.log(`[Callback] '${str}' 变成大写 '${result}' (延迟 ${delay}ms)`);
      callback(null, result); // 第一个参数是错误,第二个是结果
    }
  }, delay);
}

// 另一个基于回调的异步函数
function delayedAddSuffixCallback(str, suffix, delay, callback) {
     setTimeout(() => {
        if (typeof str !== 'string') {
            callback(new Error("输入必须是字符串"), null);
        } else {
            const result = str + suffix;
            console.log(`[Callback] '${str}' 加上后缀 '${suffix}' 变成 '${result}' (延迟 ${delay}ms)`);
            callback(null, result);
        }
     }, delay);
}


console.log("--- 不使用 Promise 的异步序列 (回调地狱) ---");


// 步骤 1 & 2 & 3: 延迟并大写
delayedUpperCaseCallback("initial value", 1000, (err1, value1) => {
  if (err1) {
    console.error("[Callback] 步骤 1&2&3 错误:", err1);
    return; // 停止后续执行
  }

  console.log("[Callback] 步骤 1&2&3 完成");

  // 步骤 4 & 5: 延迟并加后缀
  delayedAddSuffixCallback(value1, " - SUFFIX", 1000, (err2, value2) => {
      if (err2) {
        console.error("[Callback] 步骤 4&5 错误:", err2);
        return; // 停止后续执行
      }

      console.log("[Callback] 步骤 4&5 完成");

      // 步骤 6: 最终结果
      console.log("[Callback] 最终结果:", value2);

      // 假设还有更多步骤,这里的嵌套会越来越深...
      /*
      anotherAsyncOp(value2, (err3, value3) => {
          if (err3) { ... }
          yetAnotherAsyncOp(value3, (err4, value4) => {
               // 越来越深的嵌套...
          });
      });
      */
  });
});

console.log("--- 回调地狱已触发,代码继续执行 ---"); // 异步非阻塞

“回调地狱”的缺点:

  1. 代码难以阅读和理解: 随着异步步骤的增加,代码会向右侧缩进,形成难以辨认的“金字塔”结构。
  2. 错误处理困难: 错误处理逻辑分散在每一个回调函数中,需要在每个回调开始时手动检查 err 参数。如果在某个深层回调中忘记检查或处理错误,错误可能被吞没或导致程序崩溃。错误传播和集中处理非常麻烦。
  3. 代码难以维护: 添加、删除或修改异步步骤意味着需要深入嵌套结构进行修改,容易出错。
  4. 控制流复杂: 实现更复杂的控制流(如并行执行多个异步任务并等待所有完成)需要额外的计数器和标志,代码会变得更复杂。
  5. 函数职责不清: 回调函数通常既包含处理当前步骤结果的逻辑,又包含触发下一个异步操作的逻辑。

使用 Promise异步操作的优点:

  1. 解决了回调地狱 (Flattened Structure): 通过链式调用,将异步操作从垂直嵌套结构转变为水平链式结构,代码变得扁平,大大提高了可读性。
  2. 标准化的错误处理 (Centralized Error Handling): .catch() 方法可以捕获链中任何一个 Promise 的错误,无需在每个回调中单独检查错误。错误会沿着链条向下传递,直到遇到最近的 .catch()。这使得错误处理更加集中和可靠。
  3. 提高了可读性和可维护性 (Improved Readability & Maintainability): 代码流程更清晰,更容易理解异步操作的顺序和依赖关系。修改链中的

你可能感兴趣的:(javascript,前端,开发语言)