【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对象。构造函数接受一个带有两个参数的函数,分别是resolverejectresolve用于表示操作成功,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将在promise1promise2promise3都成功时成功。它将包含一个数组,其中包含所有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将与最先完成的promise1promise2promise3具有相同的状态和结果。由于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将在fetchDatatimeoutPromise中任何一个首先完成时完成。如果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对象,该构造函数接受一个带有resolvereject参数的函数,用于表示异步操作的成功和失败。
  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"

相关推荐
vvw&3 小时前
如何在 Ubuntu 22.04 上安装 Caddy Web 服务器教程
linux·运维·服务器·前端·ubuntu·web·caddy
lichong9515 小时前
【Flutter&Dart】 listView.builder例子二(14 /100)
android·javascript·flutter·api·postman·postapi·foxapi
落日弥漫的橘_5 小时前
npm run 运行项目报错:Cannot resolve the ‘pnmp‘ package manager
前端·vue.js·npm·node.js
梦里小白龙5 小时前
npm发布流程说明
前端·npm·node.js
No Silver Bullet5 小时前
Vue进阶(贰幺贰)npm run build多环境编译
前端·vue.js·npm
阿华写代码5 小时前
重新面试之JVM
jvm·面试·职场和发展
破浪前行·吴6 小时前
【初体验】【学习】Web Component
前端·javascript·css·学习·html
泷羽Sec-pp6 小时前
基于Centos 7系统的安全加固方案
java·服务器·前端
IT 古月方源6 小时前
GRE技术的详细解释
运维·前端·网络·tcp/ip·华为·智能路由器
myepicure8886 小时前
Windows下调试Dify相关组件(1)--前端Web
前端·llm