likes
comments
collection
share

为什么我们需要Promise(一)

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

前言

Promise 是 JavaScript 中的一个原生对象,用于处理异步操作,它提供了一种更优雅的方式来组织和管理异步代码,避免了回调地狱(callback hell),使得异步逻辑更加清晰和易于维护。Promise 代表一个尚未完成(或已经完成)的异步操作的结果,最终这个结果会变为 resolved(成功)或 rejected(失败)。那为什么我们需要Promise这个对象呢?

正文

众所周知Promise 有三种状态,分别是 pending(等待中)、fulfilled(已成功)和 rejected(已失败)。状态一旦从 pending 变为 fulfilled 或 rejected,就不可再变。通过 new Promise((resolve, reject) => {}) 来创建一个 Promise 实例。resolve 函数用来改变 Promise 的状态为 fulfilled,reject 函数用来改变状态为 rejected。同时Promise 支持链式调用,即 .then() 和 .catch() 方法,用于注册 fulfilled 和 rejected 状态的回调函数,并返回一个promise对象。 为什么我们需要Promise(一)

接下来我们聊聊常见的一些promise使用:

解决回调地狱

在没有Promise之前,复杂的异步操作会导致多层嵌套的回调函数,这不仅使得代码难以阅读和维护,还可能引发“回调地狱”。Promise通过链式调用的方式,使得异步逻辑更加扁平和有序。

函数回调地狱通常发生在需要进行多个连续的异步操作时,每个操作都需要等待前一个操作完成才能开始,这导致代码中充满了层层嵌套的回调函数,不仅难以阅读和维护,而且容易出错。

const data = 'data';
setTimeout(() => {
  console.log('1');
  if (data) { 
    setTimeout(() => {
      console.log('2');
      if (data) { 
        setTimeout(() => {
          console.log('3');
          console.log(data); 
        }, 1000);
      }
    }, 1000);
  }
}, 1000);

而使用Promise重写这段代码可以让异步逻辑更加清晰和易于管理。

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

delay(1000, data)
  .then(data => {
    console.log('1');
    if (data) {
      return delay(1000);
    }
  })
  .then(() => {
    console.log('2');
    // 由于data始终为真,这里直接进入下一个延迟
    return delay(1000);
  })
  .then(() => {
    console.log('3');
    console.log(data);
  })
  .catch(error => {
    console.error('发生错误:', error);
  });

更好的错误处理

Promise提供了统一的错误处理机制,即通过.catch方法可以捕获前面Promise链中的任何错误,避免了在每个回调中都需要单独处理错误的情况,使得错误处理更加集中和清晰。

const p=new Promise((resolve, reject) => {
  reject('result')
})
p.then((result) =>{
  console.log(result)
  return 'success';
})
.then((result) => {
  console.log(result)
  return 'success';
})
.catch((error) => console.log(error))

易于理解和维护

Promise的链式语法和明确的状态(pending、fulfilled、rejected)使得异步代码的流程更容易理解。开发者可以直观地看到一系列异步操作是如何依次执行的,以及它们之间的依赖关系。

  function fetchDataFromServer(url) {
  return new Promise((resolve, reject) => {
    // 模拟从服务器获取数据
    setTimeout(() => {
      if (url === 'success') {
        resolve('数据获取成功');
      } else {
        reject('数据获取失败');
      }
    }, 2000);
  });
}
function processData(data) {
  return new Promise((resolve) => {
    // 模拟数据处理过程
    setTimeout(() => {
      resolve(`处理后的数据: ${data.toUpperCase()}`);
    }, 1000);
  });
}

function saveProcessedData(processedData) {
  return new Promise((resolve) => {
    // 模拟保存处理后的数据到数据库
    setTimeout(() => {
      resolve(`数据已保存: ${processedData}`);
    }, 1500);
  });
}

// 利用Promise链处理异步操作
fetchDataFromServer('success') // 假设请求成功
  .then(data => {
    console.log(data);
    return processData(data); // 数据获取成功后进行处理
  })
  .then(processedData => {
    console.log(processedData);
    return saveProcessedData(processedData); // 数据处理完成后保存
  })
  .then(savedData => {
    console.log(savedData);
    console.log('所有操作完成');
  })
  .catch(error => {
    console.error('操作过程中出现错误:', error);
  });

支持并发和串行操作

Promise.all 允许并行执行多个Promise,并在所有Promise都成功完成后得到结果;Promise.race 可以在第一个完成的Promise(无论成功还是失败)后立即得到通知。这为管理异步任务提供了强大的工具。

Promise.all 示例

假设我们有一个场景,需要同时从两个API获取数据,只有当两个请求都成功时,我们才处理这些数据。

function fetchDataFromAPI1() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data from API 1");
        }, 2000);
    });
}
function fetchDataFromAPI2() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data from API 2");
        }, 1000);
    });
}
Promise.all([fetchDataFromAPI1(), fetchDataFromAPI2()])
    .then(results => {
        console.log("Both requests succeeded:", results);
    })
    .catch(error => {
        console.error("At least one request failed:", error);
  });

为什么我们需要Promise(一) Promise.all 等待两个API调用都成功完成,然后一起打印出结果。如果任何一个请求失败,.catch 将捕获错误。

Promise.race 示例

现在考虑一个场景,我们想尽快从两个数据源获取信息,不论哪个数据源先返回数据我们就使用那个。

    function getDataFastestWay() {
    const fastSourcePromise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data_1 already in progress");
        }, 500);
    });

    const slowSourcePromise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("Data_2 already in progress");
        }, 3000);
    });

    return Promise.race([fastSourcePromise, slowSourcePromise]);
}

getDataFastestWay().then(result => {
    console.log(result);
});

得到结果为:

为什么我们需要Promise(一)

当我们调换延迟时间:得到结果:

为什么我们需要Promise(一)

Promise.race 将在第一个完成的Promise(这里是快速数据源,在500毫秒后完成)之后立即解决,输出 较快的数据而忽略较慢的数据源响应。这样可以确保我们能够尽快得到可用的结果。

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