Promise 的场景和最佳实践

167 阅读4分钟

Promise 是 JavaScript 中用于处理异步操作的一个对象,它表示一个异步操作的最终结果,可能是成功或失败,Promise 通常用于处理异步操作,比如从服务器获取数据读取文件定时任务用户操作

基本的使用

const myPromise = new Promise((resolve, reject) => {
  const success = true;
  
  if(success) {
    resolve("操作成功!");
  } else {
    reject("操作失败。");
  }
});

myPromise
  .then(result => console.log(result))  // 如果 Promise 成功
  .catch(error => console.log(error));  // 如果 Promise 失败

常见的用法和示例

1. 链式调用 (then)

Promise 支持链式调用,可以在一个 then 后接着另一个 then,处理不同的异步操作。

new Promise((resolve, reject) => {
  setTimeout(() => resolve("第一个操作成功!"), 1000);
})
.then(result => {
  console.log(result);  // 输出: 第一个操作成功!
  return "第二个操作成功!";
})
.then(result => {
  console.log(result);  // 输出: 第二个操作成功!
});

2. 多个 Promise 的并行执行 (Promise.all)

如果你有多个独立的异步操作,并且你希望它们并行执行,可以使用 Promise.all,它会等到所有的 Promise 都完成后再继续执行。

const promise1 = new Promise(resolve => setTimeout(() => resolve("操作1完成"), 1000));
const promise2 = new Promise(resolve => setTimeout(() => resolve("操作2完成"), 2000));

Promise.all([promise1, promise2])
  .then(results => {
    console.log(results);  // 输出: ["操作1完成", "操作2完成"]
  });

3. 处理单个 Promise 的异常 (catch)

catch 方法是用来捕捉 Promise 中的异常的,它可以让你在异步操作失败时处理错误。

new Promise((resolve, reject) => {
  setTimeout(() => reject("操作失败!"), 1000);
})
.then(result => {
  console.log(result);
})
.catch(error => {
  console.log("捕获错误:", error);  // 输出: 捕获错误: 操作失败!
});

4. Promise 的竞态条件 (Promise.race)

Promise.race 会返回第一个完成的 Promise,不管它是成功还是失败。

const promise1 = new Promise(resolve => setTimeout(() => resolve("操作1完成"), 2000));
const promise2 = new Promise(resolve => setTimeout(() => resolve("操作2完成"), 1000));

Promise.race([promise1, promise2])
  .then(result => {
    console.log(result);  // 输出: 操作2完成
  });

5. 串联多个异步操作 (async/await)

async/awaitPromise 的语法糖,使得异步操作看起来像是同步的。

async function run() {
  try {
    const result1 = await new Promise(resolve => setTimeout(() => resolve("操作1完成"), 1000));
    console.log(result1);
    const result2 = await new Promise(resolve => setTimeout(() => resolve("操作2完成"), 1000));
    console.log(result2);
  } catch (error) {
    console.log("捕获错误:", error);
  }
}

run();

async 函数中,你可以使用 await 等待 Promise 的结果,这使得异步代码更简洁易读。

在实际项目中使用

1. API 请求

在开发前端应用时,向后端请求数据是最常见的异步操作。通常会使用 Promise 来处理 API 请求, 通常会使用 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);
  }
}

2. 多个异步操作并行执行 (Promise.all)

这种方式比一一等待每个请求更加高效,尤其是在多个请求之间没有依赖关系时

例如,加载多个 API 数据:

const fetchData1 = fetch('https://api.example.com/data1').then(res => res.json());
const fetchData2 = fetch('https://api.example.com/data2').then(res => res.json());

Promise.all([fetchData1, fetchData2])
  .then(([data1, data2]) => {
    console.log("数据1:", data1);
    console.log("数据2:", data2);
  })
  .catch(error => {
    console.error("加载数据失败:", error);
  });

3. 串联多个异步操作 (then)

一个异步操作完成后再执行下一个操作,这时可以使用 then 链式调用来处理。

例如,先提交表单数据,再获取结果:

function submitForm(data) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // 模拟异步表单提交
      if (data) resolve("表单提交成功");
      else reject("表单提交失败");
    }, 1000);
  });
}

submitForm({ name: 'John', age: 30 })
  .then(result => {
    console.log(result);  // 输出: 表单提交成功
    return fetch('https://api.example.com/result');  // 提交成功后获取结果
  })
  .then(response => response.json())
  .then(data => console.log("结果数据:", data))
  .catch(error => console.error("错误:", error));

4. 处理复杂的异步控制流async/await

async/awaitPromise 的语法糖,使得异步代码看起来像同步代码,简化了逻辑结构,尤其是处理多层嵌套的异步调用时非常有用。

例如,用户登录后执行后续操作:

async function loginUser(credentials) {
  try {
    const loginResponse = await fetch('https://api.example.com/login', {
      method: 'POST',
      body: JSON.stringify(credentials),
    });
    const loginData = await loginResponse.json();
    
    if (loginData.success) {
      const userProfile = await fetch('https://api.example.com/user/profile');
      const profileData = await userProfile.json();
      console.log("用户资料:", profileData);
    } else {
      console.error("登录失败");
    }
  } catch (error) {
    console.error("发生错误:", error);
  }
}

5. 错误处理和重试机制

在项目中,经常会遇到某些操作可能会失败的情况,比如网络请求失败。你可以使用 catch 来捕获错误,甚至设置重试机制。

例如,添加重试机制:

function fetchWithRetry(url, retries = 3) {
  return new Promise((resolve, reject) => {
    function attempt() {
      fetch(url)
        .then(response => {
          if (!response.ok) throw new Error("请求失败");
          return response.json();
        })
        .then(resolve)
        .catch(error => {
          if (retries > 0) {
            console.log(`重试剩余次数: ${retries}`);
            retries--;
            attempt();  // 重试
          } else {
            reject(error);  // 最终失败
          }
        });
    }
    attempt();
  });
}

fetchWithRetry('https://api.example.com/data')
  .then(data => console.log("数据:", data))
  .catch(error => console.error("最终错误:", error));

6. 动画和延时操作

有时你需要在执行一系列操作时,插入一些动画或延时操作。你可以利用 Promise 来实现这一需求。

例如,延时执行:

function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

delay(1000)  // 延迟1秒
  .then(() => console.log("1秒后执行"))
  .then(() => delay(2000))  // 再延迟2秒
  .then(() => console.log("再过2秒执行"));