当前位置: 首页 > article >正文

Node.js——异步编程(异步:阻塞与非阻塞、JavaScript执行机制、callBack hell 回调地狱,Promise、Async await)

目录

异步

阻塞与非阻塞

JavaScript执行机制

callBack hell 回调地狱

Promise

概念

特点

优缺点

基本用法 

resolve & reject 

then函数 与 catch函数

resolve & reject 参数 

例子

then链式例子

Promise.resolve()

Promise.all() 

Async await

async异步

await 


异步

阻塞与非阻塞

       阻塞时,在调用结果返回前,当前线程会被挂起,调用会一直等待数据就绪再返回。

       非阻塞需要按顺序执行的,非阻塞调用不能立刻得到结果,无论在什么情况下都会立即返回,该调用不会阻塞当前线程。

        单线程在程序执行时,程序执行路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。

JavaScript执行机制

        单线程:JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事

所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)

  • 同步任务指的是,在主线程(栈stack)上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务, 也就是就是程序的执行顺序与任务的排列顺序是一致的、同步的
  • 异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行

异步任务以回调函数的方式来实现.

异步任务的3种类型: 普通事件(click等), 网络请求(如ajax),定时器(setTimeOut等)

callBack hell 回调地狱

        ES6之前,JavaScript中异步编程分为3类:事件(如点击事件)、网络请求(如ajax)、定时器(setTimeout/setInterval)。 他们均使用回调函数来进行异步调用。

        为了实现某些逻辑经常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被成为回调地狱

Promise

概念

        Promise 是异步编程的一种解决方案,比传统的解决方案—回调函数(CallBack Function)和事件(Event)—更合理和更强大.

        所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。

        从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

特点

        对象的状态不受外界影响。Promise对象代表一个异步操作,有三种状态:

  • pending(进行中)
  • fulfilled(已成功)
  • rejected(已失败)。

        只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。

        一旦状态改变,就不会再变,任何时候都可以得到这个结果

        Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)

优缺点

        有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

        Promise也有一些缺点。

  • 首先,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 其次,如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 第三,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

基本用法 

ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。

resolve & reject 

        Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolvereject。它们是两个函数,由 JavaScript 引擎提供,不用自己部署。

        resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从 pending 变为 resolved),在异步操作成功时调用,并将异步操作的结果作为参数传递出去

        reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从 pending 变为 rejected),在异步操作失败时调用,并将异步操作报出的错误作为参数传递出去

        Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

then函数 与 catch函数

        Promise的thencatch函数就是真正用来定义resolve和reject函数体的。then和catch函数都接收一个函数作为参数.

        promise.then()函数的返回值是一个Promise对象,这是为了构造then()函数链。 promise.catch()函数的返回值也是一个Promise对象(尽管我们通常不会使用这个对象)

resolve & reject 参数 

  • 如果调用resolve函数和reject函数时带有参数,那么它们的参数会被传递给回调函数。
  • reject函数的参数通常是Error对象的实例,表示抛出的错误;
  • resolve函数的参数除了正常的值以外,还可能是另一个 Promise 实例

        p1和p2都是 Promise 的实例,但是p2的resolve方法将p1作为参数,即一个异步操作的结果是返回另一个异步操作

注意,这时p1的状态就会传递给p2 。也就是说,p1的状态决定了p2的状态

如果p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;

如果p1的状态已经是resolved或者rejected,那么p2的回调函数将会立刻执行。

例子

const p1 = new Promise(function (resolve, reject) {
    setTimeout(() => {
        reject(new Error('fail'));
    }, 3000);
});
const p2 = new Promise(function (resolve, reject) {
    setTimeout(() => {
        resolve(p1);
    }, 1000);
})

p2.then(result => console.log(result))
    .catch(error => console.log(error))

上面代码中,p1是一个 Promise,3 秒之后变为rejected。 p2的状态在 1 秒之后改变,resolve方法返回的是p1。由于p2返回的是另一个 Promise,导致p2自己的状态无效了,由p1的状态决定p2的状态。 所以,后面的then语句都变成针对后者(p1)。又过了 2 秒,p1变为rejected,导致触发catch方法指定的回调函数。

注意,调用resolve或reject并不会终结 Promise 的参数函数的执行。

then链式例子

const p = new Promise(function(resolve,reject){
    console.log("P");
    resolve("resolve p");
});

p.
    then(function(result){
        let p2 = new Promise(function(resolve,reject){
            console.log("p2",result);
            resolve("resolve p2");
        });
        return p2;
    })
    .then(function(result){
        let p3 = new Promise(function(resolve,reject){
            console.log("p3",result);
            resolve("resolve p3");
        })
        return p3;
    })
    .catch(function(error){
        console.log(error);
    })

运行结果:

解析

  1. p Promise 的创建与初始执行
    • 创建了p这个Promise实例,在其执行函数中,首先会在控制台打印出 "P",表明进入了p的执行逻辑。
    • 然后立即调用resolve函数,并传入字符串"resolve p",意味着p这个Promise会以成功状态结束,并且成功的值为"resolve p"
  2. 第一个then方法调用
    • p调用then方法,传入一个回调函数。当p成功时(也就是上面resolve被调用后),这个回调函数会被执行,它接收p成功时传递的值(这里就是"resolve p")作为参数result
    • 在这个回调函数内部,又创建了一个新的Promise实例p2,先在控制台打印出"p2"以及接收到的result值(即"resolve p"),然后调用p2resolve函数,将"resolve p2"作为成功的值传入,并且返回了p2这个Promise实例,使得后续可以继续链式调用then方法来处理p2的最终状态。
  3. 第二个then方法调用
    • 对第一个then返回的Promise(也就是p2的最终状态对应的Promise)再次调用then方法,传入另一个回调函数。当p2成功时,这个回调函数会被执行。
    • 不过这里有点小问题,在这个回调函数内部创建p3Promise时,参数命名有点混淆,按照常规习惯,回调函数的第一个参数一般用来接收上一个Promise成功传递的值,这里可以换个参数名比如prevResult会更清晰些。在这个回调函数里,先在控制台打印出"p3"以及接收到的(实际上是上一个Promise传递过来的)值,然后调用p3resolve函数并传入"resolve p3",同样返回p3这个Promise,继续构建链式调用。
  4. catch方法调用
    • 在整个链式调用的末尾添加了catch方法,用于捕获前面任何一个Promise在执行过程中出现的错误。不过在这个代码的逻辑里,由于每个Promise都是以成功状态结束的(只要按照预期执行),所以这个catch回调函数不会被执行。

Promise.resolve()

Promise.resolve() 静态方法,将给定的值转换为一个 Promise

Promise.all() 

Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例

const p = Promise.all([p1,p2,p3]);

        Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise

        p1、p2、p3都是 Promise 实例,如果不是,就会先调用Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。

        另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。

Async await

async异步

        ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

        async函数是使用async关键字声明的函数。async和await关键字让我们可以用一种更简洁的方式写出基于Promise的异步行为,而无需刻意地链式调用promise。

        本质上, async函数返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句

await 

async函数可能包含0个或者多个await表达式。

        await表达式会暂停整个async函数的执行进程并出让其控制权,只有当其等待的基于promise的异步操作被兑现或被拒绝之后才会恢复进程

        promise的解决值会被当作该await表达式的返回值。使用async / await关键字就可以在异步代码中使用普通的try / catch代码块。

        await关键字只在async函数内有效。


END 



http://www.kler.cn/a/529906.html

相关文章:

  • 贪吃蛇实现
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.9 广播陷阱:形状不匹配的深层隐患
  • 笔灵ai写作技术浅析(三):深度学习
  • MoonBit 编译器(留档学习)
  • 高温环境对电机性能的影响与LabVIEW应用
  • 【上篇】-分两篇步骤介绍-如何用topview生成和自定义数字人-关于AI的使用和应用-如何生成数字人-优雅草卓伊凡-如何生成AI数字人
  • Stable Diffusion创始人:DeepSeek没有抄袭!
  • 深入浅出并查集(不相交集合实现思路)
  • 2025年02月02日Github流行趋势
  • 【最长不下降子序列——树状数组、线段树、LIS】
  • 图像分割任务的数据预处理
  • 012-51单片机CLD1602显示万年历+闹钟+农历+整点报时
  • XML DOM 浏览器差异
  • 【AI】人工智能没那么神秘!
  • 基于WiFi的智能照明控制系统的设计与实现(论文+源码)
  • 46【什么是原生开发】
  • Vue3 表单:全面解析与最佳实践
  • C++基础学习
  • Baklib构建高效协同的基于云的内容中台解决方案
  • 《苍穹外卖》项目学习记录-Day11订单统计
  • React中useState()钩子和函数式组件底层渲染流程详解
  • 【Linux系统】进程间通信:浅谈 SystemV 标准的消息队列和信号量
  • Python - pyautogui库 模拟鼠标和键盘执行GUI任务
  • 测试中的质量度量与评估方法
  • PVE 中 Debian 虚拟机崩溃后,硬盘数据怎么恢复
  • 【大数据技术】教程02:搭建完全分布式高可用大数据集群(Hadoop+MapReduce+Yarn)