JavaScript详解使用Promise处理回调地狱与async await修饰符
作者:一个水瓶座程序猿. 发布时间:2024-04-22 22:43:58
Promise
Promise能够处理异步程序。
回调地狱
JS中或node中,都大量的使用了回调函数进行异步操作,而异步操作什么时候返回结果是不可控的,如果我们希望几个异步请求按照顺序来执行,那么就需要将这些异步操作嵌套起来,嵌套的层数特别多,就会形成回调地狱 或者叫做 横向金字塔。
案例:有a.txt、b.txt、c.txt三个文件,使用fs模板按照顺序来读取里面的内容,代码:
// 将读取的a、b、c里面的内容,按照顺序输出
const fs = require('fs');
// 读取a文件
fs.readFile('./a.txt','utf-8',(err,data)=>{
if(err) throw err;
console.log(data);
// 读取b文件
fs.readFile('./b.txt','utf-8',(err,data)=>{
if(err) throw err;
console.log(data);
// 读取c文件
fs.readFile('./c.txt','utf-8',(err,data)=>{
if(err) throw err;
console.log(data)
})
})
})
案例中,循环嵌套的代码越来越多,这就是 地狱回调
Promise简介
Promise对象可以解决 回调地狱 的问题
Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理更强大
Promise可以理解为是一个容器,里面可以编写异步程序的代码
从语法上说,Promise是一个对象,使用的时候需要 new
Promise简单使用
Promise是“承诺”的意思,实例中,它里面的异步操作就相当于一个承诺,而承诺就会有两种结果,要么完成了承诺的内容,要么失败。
所以,使用Promise,分为两大部分,首先是有一个承诺(异步操作),然后再兑现结果。
第一部分:定义"承诺"
// 实例化一个Promise,表示定义一个容器,需要给它传递一个函数作为参数,而该函数又有两个形参,通常用resolve和reject表示。该函数里面可以写异步请求的代码
// 换个角度,也可以理解为定下了一个承诺诺
let p = new Promise((resolve,reject)=>{
// 形参resolve,单词意思是 完成
// 实参reject,单词意思是 失败
fs.readFile('./a.txt','utf-8',(err,data)=>{
if(err){
// 失败,就告诉别人,承诺失败了
reject(err);
}else{
// 成功,就告诉别人,承诺实现了
resolve(data);
}
}
})
第二部分:获取"承诺"的结果
// 通过调用 p 的then方法,可以获取到上述 "承诺" 的结果
// then方法有两个函数类型的参数,参数1表示承诺成功时调用的函数,参数2可选,表示承诺失败时调用的函数
// p.then(
//(data)=>{}, // 函数类型的参数,用于获取承诺成功后的数据
// (err)=>{} // 函数类型的参数,用于承诺失败后的错误信息
//)
p.then(
(data)=>{
console.log(data);
},
(err)=>{
console.log(err);
}
)
三种状态
最初状态:pending,等待中,此时的promise结果为undefined
当resolve (value) 调用时,达到最终状态之一:fulfilled,(成功的)完成,此时可以获取结果value
当reject (error) 调用时,达到最终状态之一:rejected,失败,此时可以获取错误信息error
当达到最终的fulfilled 或 rejected 时,promise的状态就不会再改变了。
特点
当调用 resolve的时候,Promise 将到达最终的状态。 达到最终状态之后,Promise的状态就不会再改变了。
多次调用resolve函数,只有第一次有效,其他的调用都无效。
const fs = require('fs');
// 1.创建Promise对象:(承诺)
let p = new Promise((resolve,reject)=>{
resolve(123);
resolve(456); // 这次的调用无效
});
// 2.获取异步任务的结果
// p.then(函数1,[函数2]);
p.then(res =>{
console.log(res); // 123
},err =>{
console.log(err);
})
then方法的链式调用
前一个then里面返回的字符串,会被下一个then方法接收到。但是没有意义;
前一个then里面返回的Promise对象,并且调用resolve的时候传递了数据,数据会被下一个then接收到
前一个then里面如果没有调用resolve,则后续的then不会接收到任何值
const fs = require('fs');
// promise 承诺
let p1 = new Promise((resolve, reject) => {
fs.readFile('./a.txt', 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
});
});
let p2 = new Promise((resolve, reject) => {
fs.readFile('./b.txt', 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
});
});
let p3 = new Promise((resolve, reject) => {
fs.readFile('./c.txt', 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
});
});
p1.then(a => {
console.log(a);
return p2;
}).then(b => {
console.log(b); // 接收上面传递的值p2
return p3;
}).then(c => {
console.log(c) // 接收上面传递的值p3
}).catch((err) => {
console.log(err); // 如果错误,打印报错信息
});
catch方法可以统一获取到 错误信息
封装按顺序异步读取文件的函数
function myReadFile(path) {
return new Promise((resolve, reject) => {
fs.readFile(path, 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
})
});
}
myReadFile('./a.txt')
.then(a => {
console.log(a);
return myReadFile('./b.txt');
})
.then(b => {
console.log(b);
return myReadFile('./c.txt');
})
.then(c => {
console.log(c)
})
.catch((err) => {
console.log(err);
});
使用第三方模块读取文件
npm init - y
npm i then-fs 安装then-fs模块
then-fs 将 内置的fs模块封装了,读取文件后,返回 Promise 对象,省去了我们自己封装
// then-fs 模块是第三方模块,需要 npm install then-fs 下载安装的
const fs = require('then-fs');
// then-fs 对内置的fs模块进行了重写的封装。调用方法后,返回Promise对象
let p1 = fs.readFile('./files/a.txt', 'utf-8'); //
let p2 = fs.readFile('./files/b.txt', 'utf-8');
let p3 = fs.readFile('./files/c.txt', 'utf-8');
// 通过then获取结果
p1.then(res => {
console.log(res.length);
return p2;
}).then(res => {
console.log(res.length);
return p3;
}).then(res => {
console.log(res.length);
})
async和await 修饰符
ES6 — ES2015
async 和 await 是 ES2017 中提出来的。
异步操作是 JavaScript 编程的麻烦事,麻烦到一直有人提出各种各样的方案,试图解决这个问题。
从最早的回调函数,到 Promise 对象,再到 Generator 函数,每次都有所改进,但又让人觉得不彻底。它们都有额外的复杂性,都需要理解抽象的底层运行机制。
异步I/O不就是读取一个文件吗,干嘛要搞得这么复杂?异步编程的最高境界,就是根本不用关心它是不是异步。
async 函数就是隧道尽头的亮光,很多人认为它是异步操作的终极解决方案。
ES2017 提供了async和await关键字。await和async关键词能够将异步请求的结果以返回值的方式返回给我们。
async 用于修饰一个 function
async 修饰的函数,总是返回一个 Promise 对象
函数内的返回值,将自动包装在 resolved 的 promise 中
await 只能出现在 async 函数内
await 让 JS 引擎等待直到promise完成并返回结果
语法:let value = await promise对象; // 要先等待promise对象执行完毕,才能得到结果
由于await需要等待promise执行完毕,所以await会暂停函数的执行,但不会影响其他同步任务
对于错误处理,可以选择在async函数后面使用 .catch() 或 在promise对象后使用 .catch()
const fs = require('fs');
// 将异步读取文件的代码封装
function myReadFile (path) {
return new Promise((resolve, reject) => {
fs.readFile(path, 'utf-8', (err, data) => {
err ? reject(err) : resolve(data.length);
});
}).catch(err => {
console.log(err);
});
}
async function abc () {
let a = await myReadFile('./a.txt');
let b = await myReadFile('./b.txt');
let c = await myReadFile('./c.txt');
console.log(b);
console.log(a);
console.log(c);
}
abc();
错误处理
前提是得到几个Promise对象,代码如下:
let fs = require('fs');
let p1 = new Promise('./files/a.txt','utf-8');
let p2 = new Promise('./files/bbb.txt','utf-8'); // 注意:这里故意写错路径
let p3 = new Promise('./files/c.txt','utf-8');
获取Promise的结果,可以通过then获取。也可以通过async和await获取。
如果使用then获取结果,那么错误如何处理?在链式调用的尾端,加一个catch方法即可
// ----------------------------通过 then 获取结果-----------------------------
p1.then(res=>{
console.log(res);
return p2;
}).then(res=>{
console.log(res);
return p3
}).catch(err=>{
console.log(err);
})
如何使用async和await获取结果,最好的错误处理方案,就是使用 try...catch...
// ---------------------------通过 async/await 获取结果--------------------
async function abc(){
try{ // 尝试做一些事情
let r1 = await p1; // 正常获取结果
let r2 = await p2; // 这里出错了,就会抛出错误 throw err.
let r3 = await p3;
console.log(r1,r2,r3)
}catch(e){
console.log(e); // catch这里,会抓住前面try里面抛出的错误
}
}
abc();
小结
Promise是异步任务的一种解决方案
得到Promise对象
自己new Promise()
自己封装函数,函数中返回 Promise对象
使用第三方的模块或者库。比如 then-fs ,比如 axios
获取结果
通过 then 方法获取,并且通过 catch 捕获错误信息
通过 async和await的配合获取结果,并且通过 try…catch…捕获错误
来源:https://blog.csdn.net/qq_60353088/article/details/125831897


猜你喜欢
- 你可能正建立自己在网上的家-WEB页,又或者它已经被你安置在网络某一角里。无论出于什么目的,你都希望有
- 1. 概述到目前为止,程序的API对任何人都可以编辑或删除,没有任何限制。我们希望有一些更高级的行为,进行身份验证和权限分配,以确保:数据始
- 前几天玩了玩Google的Map API,感觉还不错,很简单。但凡有过任何编程经验的同学,看完以下的教程,都可以在10分钟内掌握它的主要功能
- python去除文件中重复的行,我们可以设置一个一个空list,res_list,用来加入没有出现过的字符行!如果出现在res_list,我
- 一般的网站会有很多页面,面包屑导航可以大大改善用户寻找他们的路径的方法。就可用性而言,面包屑可以减少一个网站的用户返回上一级页面的操作次数,
- python 实现单例的方法第一种方法:使用基类New 是真正创建实例对象的方法,所以重写基类的new 方法,以此保证创建对象的时候只生成一
- 阅读上一篇文章《WEB2.0网页制作标准教程(5)head区的其他设置》在开始正式内容制作之前,我们必须先了解一下web标准有关代码的规范。
- 1 概述一般地,在进行数据库设计时,应遵循三大原则,也就是我们通常说的三大范式,即第一范式要求确保表中每列的原子性,也就是不可拆分;第二范式
- 但是有时候,可以视看处进逻辑程度,可以把三者写成一个触发器,只是在其中稍作判断而已。 你可以根据从下面方法判断触发器是是处理了插入,删除还是
- 字符串在内存中是不可变的,放在只读内存段,因此你可以使用str[0]来访问,但是不能使用str[0]='a'来修改。修改字符
- 决策树原理:从数据集中找出决定性的特征对数据集进行迭代划分,直到某个分支下的数据都属于同一类型,或者已经遍历了所有划分数据集的特征,停止决策
- Book表的数据显示id title price publish_id2 Linux &nb
- 什么是品牌的视觉传达品牌,这个熟悉而又陌生的名词,有时总会让人产生误解。品牌很广,广到一个意会颇深的战略发展理念;品牌很小,小到一个清晰可见
- 很多网站都有“浏览历史”这个功能,通常都是显示在页面的一侧,特别是一些购物网站,这个功能会让用户使用网站的体验好一些;例如当当网或淘宝网都有
- 当多个事务同时持有和请求同一资源上的锁而产生循环依赖的时候就产生了死锁。死锁发生在事务试图以不同的顺序锁定资源。以StockPrice表上的
- 1.相信大家,在经过前面的初步学习之后,相信大家也想要有一个舒适的编程环境了。接下来将交给大家一个简单的配置环境Anaconda + VSC
- 位置参数这是一个求等差数列和的函数,使用必需要传入一个参数n,这就是位置参数def sum(n): sum=0 &
- 1.前言有时候,我们需要把A库A1表某一部分或全部数据导出到B库B1表中,如果系统运维工程师没打通两个库链接,我们执行T-SQL是处理数据导
- swagger介绍Swagger本质上是一种用于描述使用JSON表示的RESTful API的接口描述语言。Swagger与一组开源软件工具
- collections.namedtuple用于构造带字段名的元组。对应类型为typing.NamedTuple(可用作超类)。具名元组na