经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Omi » 查看文章
JavaScript详解使用Promise处理回调地狱与async?await修饰符
来源:jb51  时间:2022/7/19 15:03:33  对本文有异议

Promise

Promise能够处理异步程序。

回调地狱

JS中或node中,都大量的使用了回调函数进行异步操作,而异步操作什么时候返回结果是不可控的,如果我们希望几个异步请求按照顺序来执行,那么就需要将这些异步操作嵌套起来,嵌套的层数特别多,就会形成回调地狱 或者叫做 横向金字塔。

案例:有a.txt、b.txt、c.txt三个文件,使用fs模板按照顺序来读取里面的内容,代码:

  1. // 将读取的a、b、c里面的内容,按照顺序输出
  2. const fs = require('fs');
  3. // 读取a文件
  4. fs.readFile('./a.txt','utf-8',(err,data)=>{
  5. if(err) throw err;
  6. console.log(data);
  7. // 读取b文件
  8. fs.readFile('./b.txt','utf-8',(err,data)=>{
  9. if(err) throw err;
  10. console.log(data);
  11. // 读取c文件
  12. fs.readFile('./c.txt','utf-8',(err,data)=>{
  13. if(err) throw err;
  14. console.log(data)
  15. })
  16. })
  17. })

案例中,循环嵌套的代码越来越多,这就是 地狱回调

Promise简介

  • Promise对象可以解决 回调地狱 的问题
  • Promise是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理更强大
  • Promise可以理解为是一个容器,里面可以编写异步程序的代码
  • 从语法上说,Promise是一个对象,使用的时候需要 new

Promise简单使用

Promise是“承诺”的意思,实例中,它里面的异步操作就相当于一个承诺,而承诺就会有两种结果,要么完成了承诺的内容,要么失败。

所以,使用Promise,分为两大部分,首先是有一个承诺(异步操作),然后再兑现结果。

第一部分:定义"承诺"

  1. // 实例化一个Promise,表示定义一个容器,需要给它传递一个函数作为参数,而该函数又有两个形参,通常用resolve和reject表示。该函数里面可以写异步请求的代码
  2. // 换个角度,也可以理解为定下了一个承诺诺
  3. let p = new Promise((resolve,reject)=>{
  4. // 形参resolve,单词意思是 完成
  5. // 实参reject,单词意思是 失败
  6. fs.readFile('./a.txt','utf-8',(err,data)=>{
  7. if(err){
  8. // 失败,就告诉别人,承诺失败了
  9. reject(err);
  10. }else{
  11. // 成功,就告诉别人,承诺实现了
  12. resolve(data);
  13. }
  14. }
  15. })

第二部分:获取"承诺"的结果

  1. // 通过调用 p 的then方法,可以获取到上述 "承诺" 的结果
  2. // then方法有两个函数类型的参数,参数1表示承诺成功时调用的函数,参数2可选,表示承诺失败时调用的函数
  3. // p.then(
  4. // (data)=>{}, // 函数类型的参数,用于获取承诺成功后的数据
  5. // (err)=>{} // 函数类型的参数,用于承诺失败后的错误信息
  6. //)
  7. p.then(
  8. (data)=>{
  9. console.log(data);
  10. },
  11. (err)=>{
  12. console.log(err);
  13. }
  14. )

三种状态

  • 最初状态:pending,等待中,此时的promise结果为undefined
  • 当resolve (value) 调用时,达到最终状态之一:fulfilled,(成功的)完成,此时可以获取结果value
  • 当reject (error) 调用时,达到最终状态之一:rejected,失败,此时可以获取错误信息error

当达到最终的fulfilled 或 rejected 时,promise的状态就不会再改变了。

特点

当调用 resolve的时候,Promise 将到达最终的状态。 达到最终状态之后,Promise的状态就不会再改变了。

多次调用resolve函数,只有第一次有效,其他的调用都无效。

  1. const fs = require('fs');
  2. // 1.创建Promise对象:(承诺)
  3. let p = new Promise((resolve,reject)=>{
  4. resolve(123);
  5. resolve(456); // 这次的调用无效
  6. });
  7. // 2.获取异步任务的结果
  8. // p.then(函数1,[函数2]);
  9. p.then(res =>{
  10. console.log(res); // 123
  11. },err =>{
  12. console.log(err);
  13. })

then方法的链式调用

  • 前一个then里面返回的字符串,会被下一个then方法接收到。但是没有意义;
  • 前一个then里面返回的Promise对象,并且调用resolve的时候传递了数据,数据会被下一个then接收到
  • 前一个then里面如果没有调用resolve,则后续的then不会接收到任何值
  1. const fs = require('fs');
  2. // promise 承诺
  3. let p1 = new Promise((resolve, reject) => {
  4. fs.readFile('./a.txt', 'utf-8', (err, data) => {
  5. err ? reject(err) : resolve(data.length);
  6. });
  7. });
  8. let p2 = new Promise((resolve, reject) => {
  9. fs.readFile('./b.txt', 'utf-8', (err, data) => {
  10. err ? reject(err) : resolve(data.length);
  11. });
  12. });
  13. let p3 = new Promise((resolve, reject) => {
  14. fs.readFile('./c.txt', 'utf-8', (err, data) => {
  15. err ? reject(err) : resolve(data.length);
  16. });
  17. });
  18. p1.then(a => {
  19. console.log(a);
  20. return p2;
  21. }).then(b => {
  22. console.log(b); // 接收上面传递的值p2
  23. return p3;
  24. }).then(c => {
  25. console.log(c) // 接收上面传递的值p3
  26. }).catch((err) => {
  27. console.log(err); // 如果错误,打印报错信息
  28. });

catch方法可以统一获取到 错误信息

封装按顺序异步读取文件的函数

  1. function myReadFile(path) {
  2. return new Promise((resolve, reject) => {
  3. fs.readFile(path, 'utf-8', (err, data) => {
  4. err ? reject(err) : resolve(data.length);
  5. })
  6. });
  7. }
  8. myReadFile('./a.txt')
  9. .then(a => {
  10. console.log(a);
  11. return myReadFile('./b.txt');
  12. })
  13. .then(b => {
  14. console.log(b);
  15. return myReadFile('./c.txt');
  16. })
  17. .then(c => {
  18. console.log(c)
  19. })
  20. .catch((err) => {
  21. console.log(err);
  22. });

使用第三方模块读取文件

  • npm init - y
  • npm i then-fs 安装then-fs模块
  • then-fs 将 内置的fs模块封装了,读取文件后,返回 Promise 对象,省去了我们自己封装
  1. // then-fs 模块是第三方模块,需要 npm install then-fs 下载安装的
  2. const fs = require('then-fs');
  3. // then-fs 对内置的fs模块进行了重写的封装。调用方法后,返回Promise对象
  4. let p1 = fs.readFile('./files/a.txt', 'utf-8'); //
  5. let p2 = fs.readFile('./files/b.txt', 'utf-8');
  6. let p3 = fs.readFile('./files/c.txt', 'utf-8');
  7. // 通过then获取结果
  8. p1.then(res => {
  9. console.log(res.length);
  10. return p2;
  11. }).then(res => {
  12. console.log(res.length);
  13. return p3;
  14. }).then(res => {
  15. console.log(res.length);
  16. })

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()

  1. const fs = require('fs');
  2. // 将异步读取文件的代码封装
  3. function myReadFile (path) {
  4. return new Promise((resolve, reject) => {
  5. fs.readFile(path, 'utf-8', (err, data) => {
  6. err ? reject(err) : resolve(data.length);
  7. });
  8. }).catch(err => {
  9. console.log(err);
  10. });
  11. }
  12. async function abc () {
  13. let a = await myReadFile('./a.txt');
  14. let b = await myReadFile('./b.txt');
  15. let c = await myReadFile('./c.txt');
  16. console.log(b);
  17. console.log(a);
  18. console.log(c);
  19. }
  20. abc();

错误处理

前提是得到几个Promise对象,代码如下:

  1. let fs = require('fs');
  2. let p1 = new Promise('./files/a.txt','utf-8');
  3. let p2 = new Promise('./files/bbb.txt','utf-8'); // 注意:这里故意写错路径
  4. let p3 = new Promise('./files/c.txt','utf-8');

获取Promise的结果,可以通过then获取。也可以通过async和await获取。

如果使用then获取结果,那么错误如何处理?在链式调用的尾端,加一个catch方法即可

  1. // ----------------------------通过 then 获取结果-----------------------------
  2. p1.then(res=>{
  3. console.log(res);
  4. return p2;
  5. }).then(res=>{
  6. console.log(res);
  7. return p3
  8. }).catch(err=>{
  9. console.log(err);
  10. })

如何使用async和await获取结果,最好的错误处理方案,就是使用 try...catch...

  1. // ---------------------------通过 async/await 获取结果--------------------
  2. async function abc(){
  3. try{ // 尝试做一些事情
  4. let r1 = await p1; // 正常获取结果
  5. let r2 = await p2; // 这里出错了,就会抛出错误 throw err.
  6. let r3 = await p3;
  7. console.log(r1,r2,r3)
  8. }catch(e){
  9. console.log(e); // catch这里,会抓住前面try里面抛出的错误
  10. }
  11. }
  12. abc();

小结

Promise是异步任务的一种解决方案

得到Promise对象

  • 自己new Promise()
  • 自己封装函数,函数中返回 Promise对象
  • 使用第三方的模块或者库。比如 then-fs ,比如 axios

获取结果

  • 通过 then 方法获取,并且通过 catch 捕获错误信息
  • 通过 async和await的配合获取结果,并且通过 try…catch…捕获错误

到此这篇关于JavaScript详解使用Promise处理回调地狱与async await修饰符的文章就介绍到这了,更多相关JavaScript Promise回调地狱内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号