IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Node.js回调地狱及使用Promise、async和await函数的解决方法 -> 正文阅读

[JavaScript知识库]Node.js回调地狱及使用Promise、async和await函数的解决方法

1 什么是回调函数

回调函数:当一个函数作为参数传入到另外一个函数,并且该函数不会立即执行;当满足某个条件时才执行该函数。

下面代码中的fn就是回调函数:

function fn() {
    console.log("橘猫吃不胖");
}
setTimeout(fn, 1000);

2 同步任务与异步任务

同步任务:在主线程队列中,只有前一个任务完成后才会执行下一个任务

异步任务:不进入主线程队列,而是进入异步队列,前一个任务完成与否不影响后一个任务的执行(不阻塞后续任务执行的任务)

示例代码:

setTimeout(function () {
    console.log("执行了回调函数");
}, 1000);
console.log("橘猫吃不胖");

如果按照代码编写的顺序,应该先输出“执行了回调函数”,然后输入“橘猫吃不胖”。但是实际输出为:
在这里插入图片描述

3 什么是回调地狱

回调地狱:在回调函数中再嵌套回调函数的情况称为回调地狱(是实现代码顺序执行的一种操作方式)。

示例代码:

setTimeout(function () {
    console.log(1);
    setTimeout(function () {
        console.log(2);
        setTimeout(function () {
            console.log(3);
        }, 1000);
    }, 1000);
}, 1000);

依次输出1、2、3

回调地狱问题:①代码可读性差、可维护性差;②代码的扩展性差;

回调地狱的解决方法:①promise对象;②async和await函数;

4 Promise对象

4.1 概述

Promise对象是一个原生的JavaScript对象,是一种异步编程的解决方案,可以替换掉传统的回调函数解决方案。

它通过一个回调,避免更多的回调。简单说Promise就是一个容器,里面保存着某个未来才会结束的事件 (通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。

Promise有3个状态:pending(初始)、resloved(成功)、rejected(失败)。

Promise 状态发生改变,就会触发.then()里的响应函数处理后续步骤。
Promise 状态一经改变,不会再变。
Promise 实例一经创建,执行器立即执行。
在这里插入图片描述

注意:
1、Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject,它们是两个函数,又是JavaScript引擎提供,不是自己部署。
resolve函数的作用:将Promise对象的状态从“未完成”变成“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;
reject函数的作用在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

2、Promise对象的then方法用来接收处理成功时响应的数据catch方法用来接收处理失败时相应的数据

3、Promise的链式编程可以保证代码的执行顺序,前提是每一次在then做完处理后,一定要return一个Promise对象,这样才能在下一次then时接收到数据。

示例代码:

function fn(str) {
    // 创建promise对象
    let p = new Promise(function (resolve, reject) {
        // 处理异步任务
        let flag = true;
        setTimeout(function () { // 模拟异步调用
            if (flag) { // 模拟异步调用成功
                resolve(str); // 将成功的str通过resolve传递出去
            } else { // 模拟异步调用失败
                reject("操作失败"); // 将失败信息传递出去
            }
        })
    })
    return p;
}


let temp = fn(1);
// 接收resolve传递的信息
temp.then(data => { // data=1
    console.log(data);
    return fn(2);
}).then(data => { // data=2
    console.log(data);
    return fn(3);
}).then(data => { // data=3
    console.log(data);
}).catch(err => {
    console.log(err);
});

依次输出1、2、3

4.2 all的用法

Promise的all方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。用Promise.all来执行,all接收一个数组参数,里面的值最终都返回Promise对象。例如,三个异步操作的并行执行的,等到它们都执行完后才会进到then里面。all会把所有异步操作的结果放进一个数组中传给then

示例代码:

function getWidth() {
    return new Promise((resolve, reject) => {
        setTimeout(resolve(5), 1000);
    })
}

function getHeight() {
    return new Promise((resolve, reject) => {
        setTimeout(resolve(4), 1000);
    })
}

Promise.all([getWidth(), getHeight()]).then(result => {
    console.log("Result:", result);
})

Result: [ 5, 4 ]

4.3 race的用法

顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

示例代码:

function getWidth() {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, 10000, 1);
    })
}

function getHeight() {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, 2000, 2);
    })
}

function getLength() {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, 1000, 3);
    })
}

// 返回多个请求中最快得到数据的那个
Promise.race([getLength(), getHeight(), getLength()]).then(result => {
    console.log("Result:", result);
})

程序结果:Result: 3

all和race的区别:

Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标。

需要特别注意的是:Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这样最大的好处是:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。

Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

5 async和await函数

5.1 概述

Promise虽然跳出了异步嵌套的怪圈,用链式表达更加清晰,但是我们也发现如果有大量的异步请求的时候,流程复杂的情况下,会发现充满了屏幕的then,看起来非常吃力,而ES7的async/await的出现就是为了解决这种复杂的情况。

async用来修饰函数:表示函数是一个异步函数
await用来修饰函数:表示等待被修饰的函数的运行结果出来后,再执行后续的操作。必须在async修饰的函数中使用,不能单独使用。

注意:
A、可以直接获取Promise对象的resolve传递的信息,不需要使用.then
B、使用try…catch来捕获Promise对象的reject传递的异步操作失败的信息

示例:实现一个暂停功能,输入N毫秒,则停顿N毫秒后才继续往下执行

let sleep = function (time) {
    return new Promise(function (resolve, reject) {
        // time毫秒后执行resolve
        setTimeout(function () {
            resolve();
        }, time);
    })
}

let start = async function () {
    console.log(1);
    await sleep(1000);
    console.log(2);
}

start();

结果为:先输出1,然后1秒后输出2

5.2 使用async/await基本规则

  • await关键字只能在使用async定义的函数中使用
  • ?await后面可以直接跟一个 Promise实例对象(可以跟任何表达式,更多的是跟一个返回Promise对象的表达式)
  • await函数不能单独使用
  • await可以直接拿到Promise中resolve中的数据。

5.3 示例

将4.1中的示例使用async/await进行优化,代码如下:

function fn(str) {
    // 创建promise对象
    let p = new Promise(function (resolve, reject) {
        // 处理异步任务
        let flag = true;
        setTimeout(function () { // 模拟异步调用
            if (flag) { // 模拟异步调用成功
                resolve(str); // 将成功的str通过resolve传递出去
            } else { // 模拟异步调用失败
                reject("操作失败"); // 将失败信息传递出去
            }
        })
    })
    return p;
}

async function test() {
    // await直接拿到fn()返回的promise的数据,并且赋值给s1
    let s1 = await fn(1);
    let s2 = await fn(2);
    let s3 = await fn(3);
    console.log(s1, s2, s3); // 1 2 3
}

test();

await等待的虽然是promise对象,但不必写.then(…),直接可以得到返回值。

let sleep = function (time) {
    return new Promise(function (resolve, reject) {
        // time毫秒后执行resolve
        setTimeout(function () {
            resolve("Ok");
        }, time);
    })
}

let start = async function () {
    let result = await sleep(3000);
    console.log(result);
}

start();

既然.then(…)不用写了,那么.catch(…)也不用写,可以直接用标准的try…catch语法捕捉错误。

function fn(str) {
    // 创建promise对象
    let p = new Promise(function (resolve, reject) {
        // 处理异步任务
        let flag = true;
        setTimeout(function () { // 模拟异步调用
            if (flag) { // 模拟异步调用成功
                resolve(str); // 将成功的str通过resolve传递出去
            } else { // 模拟异步调用失败
                reject("操作失败"); // 将失败信息传递出去
            }
        })
    })
    return p;
}

async function test() {
    try {
        // await直接拿到fn()返回的promise的数据,并且赋值给s1
        let s1 = await fn(1);
        let s2 = await fn(2);
        let s3 = await fn(3);
        console.log(s1, s2, s3); // 1 2 3
    } catch (err) {
        console.log(err);
    }
}

test();

5.4 promise和async/await区别

  1. promise是ES6,async/await是ES7
  2. async/await相对于promise来讲,写法更加优雅
  3. reject状态:
    1. promise错误可以通过catch来捕捉,建议尾部捕获错误
    2. async/await既可以用.then又可以用try-catch捕捉
  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-03 16:03:16  更:2022-03-03 16:06:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:40:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码