JavaScript详解使用Promise处理回调地狱与async await修饰符

目录
  • Promise
    • 回调地狱
    • Promise简介
    • Promise简单使用
  • async和await 修饰符
  • 小结

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…捕获错误

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

(0)

相关推荐

  • JS动画实现回调地狱promise的实例代码详解

    1. js实现动画 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>animate</title> <style> .ball { width: 40px; height: 40px; margin-bottom: 5px; border-radius: 20px; } .ball1 { ba

  • JavaScript中异步与回调的基本概念及回调地狱现象

    目录 JavaScript异步与回调 一.前言 二.异步函数 三.回调函数 四.回调的回调 五.回调地狱 六.总结 JavaScript异步与回调 一.前言 在学习本文内容之前,我们必须要先了解异步的概念,首先要强调的是异步和并行有着本质的区别. 并行,一般指并行计算,是说同一时刻有多条指令同时被执行,这些指令可能执行于同一CPU的多核上,或者多个CPU上,或者多个物理主机甚至多个网络中. 同步,一般指按照预定的顺序依次执行任务,只有当上一个任务完成后,才开始执行下一个任务. 异步,与同步相对应

  • 深入学习JavaScript中的promise

    目录 为什么要用Promise? 使用Promise解决异步控制问题 Promise的结构 回调函数 为什么异步代码一定是回调函数结构? 刨析Promise 原型方法——catch\finally\then 为什么要在.then的回调函数中return一个Promise呢? 那如果我们不指明return返回值,它会返回什么呢?是如何实现链式调用呢? resolve和reject resolve() reject() Promise常用API——all().allSettled().any().r

  • 万字详解JavaScript手写一个Promise

    目录 前言 Promise核心原理实现 Promise的使用分析 MyPromise的实现 在Promise中加入异步操作 实现then方法的多次调用 实现then的链式调用 then方法链式调用识别Promise对象自返回 捕获错误及 then 链式调用其他状态代码补充 捕获执行器错误 捕获then中的报错 错误与异步状态的链式调用 将then方法的参数变成可选参数 Promise.all方法的实现 Promise.resolve方法的实现 finally方法的实现 catch方法的实现 完整

  • JavaScript Promise执行流程深刻理解

    目录 手撕Promise 看完收获 Promise分析 作用 特点 总体实现 resolve和reject初步实现 then方法的实现 Promise.all的实现 手撕Promise 手写一个Promise已经是一个常见的手写功能了,虽然实际工作上可能并不会用到.但是在面试时还是会经常被提起的. 看完收获 实现一个手写promise 对promise执行流程有着更深刻的理解 从底层理解proimse,应对各种面试题 Promise分析 作用 通过链式调用的方式,解决回调地狱的问题. 特点 是E

  • JS异步编程Promise对象详解

    1.单线程模型 单线程模型指的是,JavaScript 只在一个线程上运行.也就是说,JavaScript 同时只能执行一个任务,其他任务都必须在后面排队等待.注意,JavaScript 只在一个线程上运行,不代表 JavaScript 引擎只有一个线程.事实上,JavaScript 引擎有多个线程,单个脚本只能在一个线程上运行(称为主线程),其他线程都是在后台配合. JavaScript 之所以采用单线程,而不是多线程,跟历史有关系.JavaScript 从诞生起就是单线程,原因是不想让浏览器

  • Javascript的promise,async和await的区别详解

    终于把promise, async, await的区别和联系弄清楚了,看下面代码 写法1,2是promise的写法 写法6是async和await的写法 主要看第2种写法和第6中写法即可, 第2种写法是promise的典型写法,第6中写法是async, await的典型写法 // 以下三个请求依次执行 req1 = () => { return fetch("http://example.com/api/v1/get")} req2 = () => { return fet

  • JavaScript中async和await的使用及队列详情

    目录 一.宏任务和微任务的队列入门知识,可以参考之前的文章: 1.async && await概念 2.async && await基本使用 二.async&& await结合promise在面试时回遇到的队列问题 三.总结 一.宏任务和微任务的队列入门知识,可以参考之前的文章: [JavaScript的事件循环机制] 宏任务和微任务在前端面试中,被经常提及到,包括口头和笔试题 1.async && await概念 async: 使用asyn

  • JavaScript详解使用Promise处理回调地狱与async await修饰符

    目录 Promise 回调地狱 Promise简介 Promise简单使用 async和await 修饰符 小结 Promise Promise能够处理异步程序. 回调地狱 JS中或node中,都大量的使用了回调函数进行异步操作,而异步操作什么时候返回结果是不可控的,如果我们希望几个异步请求按照顺序来执行,那么就需要将这些异步操作嵌套起来,嵌套的层数特别多,就会形成回调地狱 或者叫做 横向金字塔. 案例:有a.txt.b.txt.c.txt三个文件,使用fs模板按照顺序来读取里面的内容,代码:

  • 详解C#中 Thread,Task,Async/Await,IAsyncResult的那些事儿

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部分可以同时执行:对于比较耗时的操作(例如io,数据库操作),或者等待响应(如WCF通信)的操作,可以单独开启后台线程来执行,这样主线程就不会阻塞,可以继续往下执行:等到后台线程执行完毕,再通知主线程,然后做出对应操作! 在C#中开启新线程比较简单 static void Main(string[]

  • JavaScript 详解缓动动画的封装与使用

    实现过程分析 (1)如何重复调用? 答:封装一个函数,用一次调用一次 代码分析: function animate(obj, target, callback) { //详细实现步骤 }; animate :(动画函数) obj(动画对象):给谁添加动画效果 target(目标值):移动到什么距离 callback(回调函数):同时要执行什么功能 (2)如何实现缓动效果?(缓动动画核心算法) 答:移动距离 =(目标值 - 现在盒子位置)/ 10,移动距离会慢慢变小,直至停下,就实现了缓动原理 代

  • 在vue项目中promise解决回调地狱和并发请求的问题

    场景需求: 需要同时请求5个接口 都请求成功后执行下一步操作 解决方法: 定义一个变量i=5,请求成功一个接口,让i–,直到i=0时执行下一个操作,否则不执行 axios.all 并发请求,.then(axios.spread(function(callback1, callback2)){}) promise.all 并发请求,.then(function([callback1, callback2]){}) 1.回调地狱: 函数作为参数层层嵌套 代替的为.then的链式操作 2.promis

  • Vue Promise解决回调地狱问题实现方法

    目录 问题 解决方案 问题 首先,什么是回调地狱: 层嵌套的问题. 每种任务的处理结果存在两种可能性(成功或失败),那么需要在每种任务执行结束后分别处理这两种可能性. 当一个接口需要依赖另一个接口的请求数据时,通常有两种解决方式 将请求数据的接口设为同步,之后调另一个接口 在请求数据接口的成功回调里调另一个接口 这两种问题在回调函数时代尤为突出.Promise 的诞生就是为了解决这两个问题. 典型的高阶函数,将回调函数作为函数参数传给了readFile.但久而久之,就会发现,这种传入回调的方式也

  • 详解JNA中的回调方法

    目录 简介 JNA 中的 Callback callback 的应用 callback 的定义 callback 的获取和应用 在多线程环境中使用 callback 总结 简介 什么是 callback 呢?简单点说 callback 就是回调通知,当我们需要在某个方法完成之后,或者某个事件触发之后,来通知进行某些特定的任务就需要用到 callback 了. 最有可能看到 callback 的语言就是 javascript 了,基本上在 javascript 中,callback 无处不在.为了

  • 详解ES6 Promise的生命周期和创建

    一:Promise的概念 Promise的中文意思是'承诺',什么叫承诺?承诺就是现在没有发生,在将来的某个时刻一定会发生的事情. 放在编程语言的环境下,Promise就是异步事件的结果的占位符.我们不用去管异步事件的结果什么时候来,只需要关心异步事件的结果产生的时候,你想要做什么就对了. 二:Promise的生命周期 异步事件不是立即执行程序,它的结果可能要在动作发生后一段时间才到,所以它有个生命周期.例如用电饭锅煮米饭,从[米下锅开始定时]到[定时结束],这是煮米饭的生命周期. 一个Prom

  • 详解vue-resource promise兼容性问题

    背景 其实这个问题在之前的项目开发中就出现过,但是当初只解决问题了,并没有针对问题作总结:于是乎今天踩到了自己埋的坑,所以决定记录一下.那么到底是什么问题呢?就是"在安卓低版本,如果你在vue项目中使用了vue-resource(vue-resource是什么?它是一个具有ajax功能的第三方npm包),那么http请求和响应都是正常的,但是,注意,但是来了啊!!但是响应进不去promise回调",下面我们来具体看一下 正题 一.Vue Resource如何使用? 大家都知道,我们在v

  • JS回调函数原理与用法详解【附PHP回调函数】

    本文实例讲述了JS回调函数原理与用法.分享给大家供大家参考,具体如下: JS回调函数 何为回调函数,官方解释:当程序跑起来时,一般情况下,应用程序(application program)会时常通过API调用库里所预先备好的函数.但是有些库函数(library function)却要求应用先传给它一个函数,好在合适的时候调用,以完成目标任务.这个被传入的.后又被调用的函数就称为回调函数(callback function). 通常将一个函数B传入另一个函数A,并且在需要的时候再调用函数A. 说白

  • 详解ES6 Promise对象then方法链式调用

    promise俗称链式调用,它是es6中最重要的特性之一 简单的说可以不停的then调用嵌套在调用(异步之后,链式调用方式执行回调),这种操作方式称为promise then()方法的作用是Promise实例添加解决(fulfillment)和拒绝(rejection)状态的回调函数.then()方法会返回一个新的Promise实例,所以then()方法后面可以继续跟另一个then()方法进行链式调用. let p = new Promise((resolve, reject) => { set

随机推荐