【ES6】聊聊用“Promise优雅地处理异步操作

序言

在上篇文章中我们聊到了异步进行的利与弊,以及用回调来处理异步,但是这种处理方式会形成"回调地狱",虽不会造成内存泄漏,但是代码复杂而且维护难度较高。那么在今天我会为大家介绍一下ES6新增的一个对象------Promise。

Promise概念

JavaScript作为一种单线程且异步的语言,处理异步操作,可能是具有挑战性的。对js执行代码的底层逻辑不太熟悉的朋友可以去翻翻我的这篇文章【前端面试】深入理解 JS 中 调用栈 作用域链 闭包

如何优雅地处理异步呢?这时Promise就发挥了作用,提供了一种更有结构和可靠的方式来处理异步代码。接下来让我们来先对Promise进行一个深入的认识。

Promise的基础知识

1. Promise是什么?

在JavaScript中,Promise是一种处理异步操作的对象,它表示一个在未来某个时刻会完成或失败的操作。Promise提供了更清晰、更结构化的方式来处理异步代码,避免了回调地狱(Callback Hell)的问题。

2. Promise的三个状态

Promise有三个状态:

  • Pending(进行中) : 初始状态,表示操作正在进行中。
  • Fulfilled(已成功) : 表示操作已经成功完成。
  • Rejected(已失败) : 表示操作失败。

总结:Promise表示异步操作的最终完成或失败。它有三个状态:进行中(Pending)、已成功(Fulfilled)、已失败(Rejected)。创建Promise时,它开始处于进行中状态,并根据异步操作的结果转换为已成功或已失败状态,并且这个转换过程是不可逆的,只允许进行一次。接下来让我们再来了解一下如何创建一个Promise对象。

Promise 创建及使用方法

创建Promise对象

使用new Promise()构造函数创建一个Promise对象。构造函数接受一个带有两个参数的函数,分别是resolve和reject。resolve用于表示操作成功,reject用于表示操作失败。

javascript 复制代码
const myPromise = new Promise((resolve, reject) => {
    // 异步操作
    if (/* 操作成功 */) {
        resolve("操作成功");
    } else {
        reject("操作失败");
    }
});

链式使用Promise

Promise可以链接在一起,以更灵活地组织和控制异步操作。下面我们借助LOL全球总决赛四强的一次比赛来模拟链式使用Promise处理比赛进行顺序的异步操作。

javascript 复制代码
function bo1(){
    return new Promise((resolve,reject) => {// promiese----ES6新增构造函数
        setTimeout(() => {
            console.log('JDG比赛第一把输了!');
            resolve('比分0:1')
        }, 2000);
    }) 
}

function bo2(){
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            console.log('JDG比赛第二把赢了!');
            resolve('比分1:1')
        }, 1000)
    })
}

function bo3(){
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            console.log('JDG比赛第三把输了!');
            resolve('比分1:2')
        }, 500)
    })
}

function bo4(){
    return new Promise((resolve,reject) => {
        setTimeout(() => {
            console.log('JDG比赛第四把输了!');
            resolve('比分:1:3,坐飞机回家了!')
        }, 300)
    })
}

bo1()
.then((res) => {
    console.log(res);
    return bo2()
})
.then(res2 => {
    console.log(res2);
    return bo3()
})
.then(res3 => {
    console.log(res3);
    return bo4()
})
.then(res4 => {
    console.log(res4);
})

我们去浏览器运行这段代码看看结果:

虽然每段代码执行都是异步进行的,且每一个函数自己有不同的执行时间,但是当我们对他们进行Promise链式调用的时候,结果就能按照我们预期的效果进行展现,不仅如此,相比回调地狱,Promise还提供了各种方法来满足我们的需求。比如捕获错误,省去像无头苍蝇一般到处乱找BUG的情况。接下来我会为大家一一详细介绍常用的方法。

promise常用方法

Promise.all()

在JavaScript中,Promise.all方法是Promise对象上的一个静态方法,用于将多个Promise对象包装成一个新的Promise对象。这个新的Promise对象的状态取决于所有原始Promise对象的状态,只有当所有的原始Promise对象都成功时,新的Promise对象才会成功。让我们深入了解Promise.all方法的语法、用法和一些示例。

1. 语法和用法

Promise.all方法的基本语法如下:

javascript 复制代码
Promise.all(iterable);
  • iterable: 一个可迭代对象,通常是包含Promise对象的数组。

Promise.all返回一个新的Promise对象,其状态和值取决于所有原始Promise对象的状态。如果所有原始Promise对象都成功,则新的Promise对象将成功,并包含一个包含所有原始Promise结果的数组。如果任何一个原始Promise对象失败,则新的Promise对象将失败,并包含第一个失败的原始Promise对象的错误信息。

2. 示例演示

让我们通过一个简单的例子来说明Promise.all的用法:

javascript 复制代码
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Promise 1 resolved');
    }, 1000);
});

const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Promise 2 resolved');
    }, 500);
});

const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Promise 3 resolved');
    }, 800);
});

const allPromise = Promise.all([promise1, promise2, promise3]);

allPromise
    .then((results) => {
        console.log(results); // 所有Promise成功的结果数组
    })
    .catch((error) => {
        console.error(error); // 第一个失败的Promise的错误信息
    });

在这个例子中,allPromise将在promise1、promise2和promise3都成功时成功。它将包含一个数组,其中包含所有Promise对象的成功结果。如果任何一个Promise对象失败,allPromise将失败,并包含第一个失败的Promise对象的错误信息。

3. 使用场景

Promise.all通常用于处理多个相关但相互独立的异步操作,等待它们全部完成后再执行下一步操作。一个常见的应用场景是同时向多个API端点请求数据,然后在所有数据都返回后进行处理。

javascript 复制代码
const fetchData1 = fetch('https://api.example.com/data1');
const fetchData2 = fetch('https://api.example.com/data2');
const fetchData3 = fetch('https://api.example.com/data3');

const allDataPromise = Promise.all([fetchData1, fetchData2, fetchData3]);

allDataPromise
    .then((responses) => {
        return Promise.all(responses.map(response => response.json()));
    })
    .then((data) => {
        console.log(data); // 所有API端点返回的数据数组
    })
    .catch((error) => {
        console.error(error); // 如果任何一个API请求失败,输出错误信息
    });

在这个例子中,allDataPromise等待所有API请求完成,然后将它们的响应用json方法处理,最终输出一个包含所有API端点数据的数组。

总体而言,Promise.all是一个强大的工具,用于管理和协调多个异步操作,使得在它们全部完成后执行下一步操作变得更加简单。

Promise.race()

JavaScript中的Promise.race方法是Promise对象上的一个静态方法,它提供了一种处理多个Promise对象的方式,以便创建一个新的Promise对象,其状态和结果取决于最先完成的原始Promise对象。让我们深入了解这个有用的方法。

1. 语法和用法

Promise.race方法的基本语法如下:

javascript 复制代码
Promise.race(iterable);
  • iterable: 一个可迭代对象,通常是包含Promise对象的数组。

Promise.race返回一个新的Promise对象,其状态和值将取决于第一个完成的原始Promise对象。如果第一个Promise对象成功,新Promise对象将成功;如果第一个Promise对象失败,新Promise对象将失败。

2. 示例演示

让我们通过一个简单的例子来说明Promise.race的用法:

javascript 复制代码
const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Promise 1 resolved');
    }, 1000);
});

const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('Promise 2 resolved');
    }, 500);
});

const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('Promise 3 rejected');
    }, 800);
});

const racePromise = Promise.race([promise1, promise2, promise3]);

racePromise
    .then((result) => {
        console.log(result); // 第一个完成的Promise结果
    })
    .catch((error) => {
        console.error(error); // 如果第一个完成的Promise失败,则输出错误信息
    });

在这个例子中,racePromise将与最先完成的promise1或promise2或promise3具有相同的状态和结果。由于promise2首先完成,racePromise的状态将是Fulfilled,并输出Promise 2 resolved。如果promise3首先完成,racePromise将变为Rejected,并输出Promise 3 rejected。

3. 使用场景

Promise.race常用于处理多个异步操作,但只关心最快完成的情况。例如,可以用它来设置一个超时机制,如果某个异步操作在一定时间内没有完成,就采取相应的措施。

javascript 复制代码
const fetchData = new Promise((resolve, reject) => {
    // 模拟异步操作
    setTimeout(() => {
        resolve('Data fetched successfully');
    }, 2000);
});

const timeoutPromise = new Promise((resolve, reject) => {
    // 设置超时时间为1秒
    setTimeout(() => {
        reject('Timeout: Operation took too long');
    }, 1000);
});

const resultPromise = Promise.race([fetchData, timeoutPromise]);

resultPromise
    .then((result) => {
        console.log(result); // Data fetched successfully
    })
    .catch((error) => {
        console.error(error); // Timeout: Operation took too long
    });

在这个例子中,resultPromise将在fetchData和timeoutPromise中任何一个首先完成时完成。如果fetchData在1秒内完成,它将成功并输出Data fetched successfully,否则,timeoutPromise将导致resultPromise失败,并输出Timeout: Operation took too long。

总体而言,Promise.race是处理异步操作的一种强大工具,特别适用于需要及时响应的情况。

总结

Promise是JavaScript中处理异步操作的一种机制,它提供了更清晰、更结构化的方式来编写异步代码,避免了回调地狱的问题。以下是Promise处理异步的关键概括:

  1. 状态和状态转换: Promise有三个状态,分别是进行中(Pending)、已成功(Fulfilled)、已失败(Rejected)。一旦Promise状态从Pending变为Fulfilled或Rejected,就不会再改变。
  2. 创建Promise对象: 使用new Promise()构造函数创建一个Promise对象,该构造函数接受一个带有resolve和reject参数的函数,用于表示异步操作的成功和失败。
  3. 处理Promise状态: 使用.then()处理成功的情况,使用.catch()处理失败的情况。这些方法允许更清晰地定义在异步操作完成后要执行的代码。
  4. Promise链: 使用.then()方法进行链式调用,允许按顺序执行多个异步操作,每个操作都依赖前一个操作的结果。
  5. Promise.all方法: Promise.all方法用于将多个Promise对象合并成一个新的Promise对象。新的Promise对象的状态和值取决于所有原始Promise对象的状态,只有当所有原始Promise对象都成功时,新的Promise对象才会成功。
  6. Promise.race方法: Promise.race方法用于将多个Promise对象合并成一个新的Promise对象,新的Promise对象的状态和值取决于最先完成的原始Promise对象。

如果这篇文章感觉对你有用的话,给作者一点鼓励点个赞吧♥

这个专栏在持续更新更多有用的干货中,关注➕收藏 Coding不迷茫

所有文章的源码,给作者的开源git仓库点个收藏吧: gitee.com/cheng-bingw...

更多干货内容:【ES6】聊聊ES6 新增特性中的引用数据类型"Set - Map" 以及面试热考点"Weakset"

相关推荐
zach11 分钟前
前端落地实战:Next.js 项目 Nginx + PM2 生产部署全流程(附踩坑总结)
前端·pm2·next.js
overmind20 分钟前
oeasy教h5前端001 修改的乐趣_直接修改在线网页
前端
只睡四小时25 分钟前
JS 手写 D-pad 空间导航:电视端焦点引擎实战
android·开发语言·前端·javascript·ecmascript·hls·空间导航
恋猫de小郭28 分钟前
Android 原生的 Compose A2UI 也来了,你还抱着 XML 养老吗?
android·前端·flutter
庄园特聘拆椅狂魔29 分钟前
从连连看到粒子消散——前端小游戏与视觉特效的技术选型
前端
JudithHuang1 小时前
React 常用 Hooks
前端·react.js·前端框架
sycmancia2 小时前
Qml——Window元素使用
前端
每天都好困2 小时前
03 AR 投影与误差修正:把地图上的点贴到视频画面上
前端
每天都好困2 小时前
04 地图图层封装:像管理 Photoshop 图层一样管理地图元素
前端
去伪存真2 小时前
构建你的第一个 DevOps AI Agent:自动化捕获、分析并修复 CI 故障
前端·agent