Node.js:ES6 模块化 Promise

Node.js:ES6 模块化 & Promise

    • ES6 模块化
      • 默认导入导出
      • 按需导入导出
    • Promise
      • 构造
      • 状态
      • then
      • cache
      • all
      • race
      • async
      • await


ES6 模块化

Node.js中,遵循的是CommonJS的模块化规范,使用require方法导入模块,使用moudule.exports导出模块。

JavaScript中,存在很多模块化的规范,后端常用CommonJS,前端常用AMDCMD等。

这导致JavaScript没有一个统一的方式完成模块化,增加了学习成本,为此推出了统一的ES6模块化规范,不论前后端,只要是JavaScript都可以支持这套规范。

规定:

  1. 每个.js文件都是一个独立的模块
  2. 导入模块使用import
  3. 共享模块成员使用export

想要在Node.js中使用ES6模块化规范,需要在配置文件package.json中添加一个键值对:

"type": "module"

这样就可以支持ES6模块化了。

测试:

  • test.js
console.log("hello world")
  • main.js
import './test.js'

main.js中,直接import另一个文件,就会把被导入文件内部的所有代码执行一次

运行main.js输出结果:

hello world

但是直接import是拿不到模块内部的变量的,这需要在模块内部进行导出操作。


默认导入导出

默认导出语法:

export default {导出变量1,导出变量2,......
}

默认导入语法:

import 变量名 from '模块'

这相当于const 变量名 = require('模块'),只要在模块内部通过默认导出,就可以在外部导入模块时,拿到指定变量。

  • test.js
let n1 = 10
let n2 = 20
function hello() {console.log('hello world')
}export default {n1,hello
}

test.js中,把变量n1和函数hello进行了默认导出。

  • main.js
import test_module from './test.js'
console.log(test_module)

main.js中,默认导入了test.js,结果导入在test_module中。

输出结果:

{n1: 10, hello: ƒ}

拿到了n1hello两个值,但是没有拿到n2,因为n2没有被导出。


按需导入导出

以上的所有功能,都可以通过require进行完成,ES6还支持按需的导入导出,只导入需要的变量。

按需导出:

export 变量

在声明变量时,可以添加export关键字,那么该变量就可以被按需导出,只有在用户指定的情况下,该变量才会被导出。

按需导入:

import { 变量1, 变量2 } from '模块'

导入时,使用{}进行按需导入,把要导入的变量名写在内部,表示只导入这些变量。

  • test.js
let n1 = 10
export let n2 = 20export function bye() {console.log('bye bye')
}export default {n1
}

默认导出n1,按需导出n2bye

  • main.js
import {n2, bye} from './test.js'console.log(n2)
bye()

按需导入n2bye

输出结果:

20
bye bye

成功导入了两个按需导出的变量,但是没有导入n1,因为n1是默认导出的。

main.js进行默认导入:

import test_moudle from './test.js'
console.log(test_moudle)

输出结果:

{n1: 10}

此时只能拿到n1,因为n2bey是按需导出的,默认导出不会导出按需导出的变量

另外的,导入时可以同时执行按需导出和默认导出:

import test_moudle, { n2 } from './test.js'

以上代码默认导出了test_moudle,并按需导出了n2,这个过程中bye没有被导出,如果想要导出bey,只需要加到{}内部:

import test_moudle, { n2, bye } from './test.js'

对于按需导出的变量,{}内部的变量名必须和模块内部的变量名完全一致。但是这有可能导致命名冲突。

示例:

import { n2, bye } from './test.js'
const n2 = 1

在按需导入时,导入了n2变量,但是当前模块也有n2变量,这就导致命名冲突。

在按需导入时,可以进行变量重命名:

import { 变量名 as 新名 } from '模块'

示例:

import { n2 as test_n2, bye } from './test.js'
const n2 = 1

按需导入n2时,将n2重命名为test_n2,这样就不会发生命名冲突了。


Promise

由于JavaScript是单线程异步的模型,如果想让函数按照指定顺序执行,常常采用回调函数的形式。

比如以下三个函数:

function asyncOperation1(next) {console.log('Operation 1 completed')next()
}function asyncOperation2(next) {console.log('Operation 2 completed')next()
}function asyncOperation3(next) {console.log('Operation 3 completed')next()
}

每个函数都接受一个next回调,现在希望按顺序依次执行这三个函数:

asyncOperation1(() => {asyncOperation2(() => {asyncOperation3(() => {console.log('All operations completed');});});
});

此时就会变成一个多级嵌套的结构,虽然可以解决问题,但是这样的代码很不优雅,看起来也很费劲。

为此ES6推出了Promise,用于解决这种多级嵌套结构,让函数之间解耦。

使用Promise优化后,代码大致如下:

asyncOperation1().then(() => asyncOperation2()).then(() => asyncOperation3()).then(() => {console.log('All operations completed');})

可以看到,asyncOperation2asyncOperation3这两个函数,都在同一级缩进中,就算后面再加多少个函数,都只会在这样的同一级缩进中:

asyncOperation1().then(() => asyncOperation2()).then(() => asyncOperation3()).then(() => asyncOperation4()).then(() => asyncOperation5()).then(() => asyncOperation6()).then(() => asyncOperation7()).then(() => {console.log('All operations completed');})

这样可以保证1 - 7按顺序调用,并且避免了多级嵌套的情况。


构造

Promise的本质是一个构造函数,格式如下:

Promise((resolve, reject) => {// 函数体
})

该构造接收一个函数,函数内包含两个参数:

  • resolve:表示函数执行成功
  • reject:表示函数执行失败

示例:

const p = new Promise((resolve, reject) => {let n = Math.floor(Math.random() * 10) + 1if (n % 2 == 0)resolve()elsereject()
})

这是一个随机函数,生成1 - 10的随机数n,依据n % 2的结果,选择调用不同的函数。

这个Promise函数构造出一个对象,可以使用对象的.then方法传入resolvereject对应的回调函数。

示例:

p.then(() => {console.log("success!")
}, () => {console.log("fail!")
})

p是刚才构造出的对象,通过p.then可以调用刚才的函数,并且通过then的两个参数,传入回调函数。

现在就可以尝试优化之前的asyncOperation函数:

function asyncOperation1() {return new Promise((resolve) => {console.log('Operation 1 completed');resolve();});
}

asyncOperation1要调用.then方法,来指定下一个执行的函数,因此该函数的返回值必须是一个Promise对象,在对象内部,调用resolve就是在调用下一个函数。

以上函数没有用到reject这个参数,因为它不需要分情况回调不同的函数,所以只需要一个参数即可。

类似的,后两个函数如下:

function asyncOperation2() {return new Promise((resolve) => {console.log('Operation 2 completed');resolve();});
}function asyncOperation3() {return new Promise((resolve) => {console.log('Operation 3 completed');resolve();});
}

这样就形成了一个链式调用,上一个函数返回一个Promise对象,并调用.then方法传入下一个函数。这样就可以让函数按顺序执行,且可以一直追加.then方法来拓展函数调用链条,避免嵌套式回调。

asyncOperation1().then(() => asyncOperation2()).then(() => asyncOperation3()).then(() => {console.log('All operations completed');})

状态

Promise的功能,远远不止处理回调,其还有很多其它功能。

示例:

const p = new Promise((resolve, reject) => {throw("err");resolve()
})p.then(() => {console.log("success!")console.log(p)
}, () => {console.log("fail!")console.log(p)
})

Promise中,只调用了resolve方法,但是在调用该方法之前,throw了一个异常,最后调用的不是resolve而是reject

输出结果:

fail!
Promise {[[PromiseState]]: 'rejected', [[PromiseResult]]: 'err', Symbol(async_id_symbol): 71, Symbol(trigger_async_id_symbol): 36}

可以看到,输出结果中,有一个[[PromiseState]]属性,这个属性就是记录当前的Promise状态,最终根据这状态,来决定调用resolve还是reject

[[PromiseResult]],存储着上一个函数的输出结果,下一个函数可以通过Promise对象访问到这个结果,这个可以直接在调用resolve()reject()时作为参数传入。

示例:

const p = new Promise((resolve, reject) => {resolve('OK!')
})p.then(() => {console.log("success!")console.log(p)
}, () => {console.log("fail!")console.log(p)
})

这一次只调用了resolve函数,并且调用时传入了一个参数OK!,输出结果:

success!
Promise {[[PromiseState]]: 'fulfilled',[[PromiseResult]]: 'OK!',Symbol(async_id_symbol): 71,Symbol(trigger_async_id_symbol): 36}

这一次的状态为fulfilled表示成功,结果为OK!

Promise分为以下三种状态:

  1. pending:默认状态,表示还不确定成功或失败
  2. fulfilled:成功状态
  3. rejected:失败状态

当一个Promise对象创建时,为默认状态,如果调用了resolve()就会变为fulfilled状态,调用reject()就会变为rejected状态。

Promise对象调用then方法时,依据当前的[[PromiseState]]来决定下一个函数调用resolve还是reject

要注意的是,一个Promise对象的状态只会切换一次,并且只能从pending切换为fulfilledrejected两者之一,后两者之间不能随意切换。

切换状态的方法:

  1. resolve:从pending变为fulfilled
  2. reject:从pending变为rejected
  3. thorw:抛出一个异常,从pending变为rejected

示例:

const p = new Promise((resolve, reject) => {resolve('OK!')reject('error')
})

这个函数中,最后p的状态为fulfilled,因为先执行了resolve,从pending切换为fulfilled状态。第二次调用rejectfulfilled不能再切换为rejected状态了。


then

Promise构造中,要求用户传入一个函数:

let p1 = new Promise((resolve, reject) => {resolve('p1 OK!')
})

此时整个(resolve, reject) => {}函数会立刻执行一次,有人就有疑问了,resolvereject不是两个回调函数吗?如果没有通过.then传入回调函数,p1怎么知道自己要回调哪个函数?

刚讲解了Promise对象的状态,可以得知resolvereject的本质是在改变Promise对象的状态,其实根本就没有去调用函数。

then的作用,就是等待构造函数内部的函数执行完毕,再进行下一步操作。

示例:

let p1 = new Promise((resolve, reject) => {setTimeout(()=>{resolve('p1 OK!')}, 10000)
})console.log(p1)

p1中设置了一个延时,10s之后把自己的状态设为成功。但是由于此时构成异步,console.log(p1)会比 resolve('p1 OK!')先执行,输出结果:

Promise {[[PromiseState]]: 'pending',[[PromiseResult]]: undefined, Symbol(async_id_symbol): 71, Symbol(trigger_async_id_symbol): 36}

此时输出p1的状态就是pending,表示还不确定状态,结果也为undefined

修改代码:

let p1 = new Promise((resolve, reject) => {setTimeout(()=>{resolve('p1 OK!')}, 10000)
})p1.then(()=>{console.log(p1)
})

此时p1.then,会等待到p1的状态确定后再执行,保证调用的顺序。

输出结果:

Promise {[[PromiseState]]: 'fulfilled', [[PromiseResult]]: 'p1 OK!', Symbol(async_id_symbol): 71, Symbol(trigger_async_id_symbol): 36}

一个Promise也可以绑定多个then,一旦Promise对象的状态完成切换,所有的then都会被执行


cache

cache方法,可以指定一个错误处理函数reject,当使用多个.then调用Promise时,可以使用.cache处理所有Promise发生的错误。

示例:

function func1(){return new Promise((resolve, reject) => {console.log('func1 successed')resolve('OK!')})
}function func2(){return new Promise((resolve, reject) => {console.log('func2 successed')resolve('OK!')})
}function func3(){return new Promise((resolve, reject) => {console.log('func3 error')reject('error')})
}function func4(){return new Promise((resolve, reject) => {console.log('func4 successed')resolve('OK!')})
}function funcErr() {console.log("something err happend")
}

这厮四个函数,他们都返回一个Promise对象,最后一个函数funcErr用于处理错误,那么调用逻辑就写为:

func1().then(func2, funcErr).then(func3, funcErr).then(func4, funcErr)

输出结果:

func1 successed
func2 successed
func3 error
something err happend

输出完func3后,代码出现错误,此时触发funcErr,不再执行func4

以上代码有两个优化点:

  1. 所有函数都使用funcErr处理错误,能不能一次性给他们指定错误处理函数
  2. func3出错后,func4不再执行了,如果在funcErr内部修理好了错误,能不能继续执行func4

以上问题都可以通过cache解决。

func1().then(func2).then(func3).then(func4).catch(funcErr)

在所有then的末尾,添加一个cache,传入funcErr参数,只要有任意一个函数调用出错,都会执行cache内部的函数。

输出结果:

func1 successed
func2 successed
func3 error
something err happend

可以看到,cache捕捉到了func3产生的错误,但是func4依然没有执行。

为此,可以把.cache的位置修改一下:

func1().then(func2).then(func3).catch(funcErr).then(func4)

.cache执行完毕后,会返回一个Promise对象,状态为成功,这样就可以继续调用func4了:

输出结果:

func1 successed
func2 successed
func3 error
something err happend
func4 successed

all

allPromise的一个方法,其传入一个Promise对象的数组。

语法:

Promise.all([Promise, Promise ...])

如果所有Promise对象执行成功,那么all返回一个成功的Promise对象,如果任意一个Promise对象执行失败,返回一个失败的Promise对象。

这里所谓的成功或失败的Promise对象,其实就是对象的状态分别为fulfilledrejected

示例:

let p1 = new Promise((resolve, reject) => {resolve('p1 OK!')
})let p2 = new Promise((resolve, reject) => {resolve('p2 OK!')
})let p3 = new Promise((resolve, reject) => {resolve('p3 OK!')
})let ret = Promise.all([p1, p2, p3])ret.then(() => {console.log(ret)
})

此处定义了三个Promise,最后使用Promise.all([p1, p2, p3]),得到一个Promise对象ret,输出这个ret

此处注意,最后不能直接console.log(ret),要放进.then中,因为定义p1p2p3的过程是异步的,执行console.log(ret)时,前三者可能还没有执行完。

输出结果:

在这里插入图片描述

收到的Promise对象中,状态为成功,结果是一个数组,分别是p1 p2 p3的输出结果。

修改代码:

let p1 = new Promise((resolve, reject) => {resolve('p1 OK!')
})let p2 = new Promise((resolve, reject) => {reject('p2 err!')
})let p3 = new Promise((resolve, reject) => {resolve('p3 OK!')
})let ret = Promise.all([p1, p2, p3])ret.catch(error => {console.log(ret)
});

此处p2改为错误的Promise,并且最后变为ret.cache,如果不捕获错误的话会报错。

输出结果:

在这里插入图片描述

输出结果是p2的内容。

返回值:

  1. 如果数组中所有的Promise成功,返回的Promise也成功,结果是一个数组,包含所有Promise的结果
  2. 如果数组中任意一个Promise失败,返回的Promise也失败,结果是失败的那个元素的结果
  3. 如果数组中多个Promise失败,返回的Promise也失败,结果是最早失败的那个元素的结果

race

race也是Promise的一个方法,其传入一个Promise对象的数组。

语法:

Promise.race([Promise, Promise ...])

race意为赛跑,当数组中任意一个Promise确定成功或失败,race直接返回这个Promise

其实就是得到执行速度最快的Promise


async

async是一个关键字,被async修饰的函数,返回值会变成一个Promise对象。

语法:

async function name{
}

只需要在function前增加一个async关键字即可。

返回值如下:

  1. 如果返回值是一个非Promise类型,或者没有返回值,那么返回一个状态为成功的Promise对象
  2. 如果返回值是一个Promise类型,原先是什么就返回什么
  3. 如果函数抛异常了,返回一个状态为失败的Promise对象

示例:

async function test()
{return "hello"
}console.log(test())

输出结果:

Promise {[[PromiseState]]: 'fulfilled', [[PromiseResult]]: 'hello', Symbol(async_id_symbol): 71, Symbol(trigger_async_id_symbol): 36}

此时返回值变为了Promise对象,类型是成功,原本的返回值hello存储在了[[PromiseResult]]中。

测试抛异常:

async function test()
{throw "error"
}console.log(test())

输出结果:

Promise {[[PromiseState]]: 'rejected', [[PromiseResult]]: 'error',Symbol(async_id_symbol): 71, Symbol(trigger_async_id_symbol): 36}

得到的返回值就是一个rejectedPromise对象,抛出的异常存储在了[[PromiseResult]]中。


await

await是一个表达式,可以快速拿到Promise存储的值。await必须写在async函数中,但是async函数内可以没有await

await右侧接收一个表达式:

  1. 如果表达式是成功的Promise对象,返回对象存储的值
  2. 如果表达式是失败的Promise对象,抛出一个异常
  3. 如果表达式不是Promise对象,直接返回表达式值

示例:


async function test()
{let p = new Promise((resolve, reject)=>{resolve('OK!')})console.log(await p)console.log(await 'hello')
}
test()

输出结果:

OK!
hello

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/703.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

利用STM32控制3D打印机时优化打印精度的教学

引言 在3D打印的过程中,打印精度直接影响到最终产品的质量与性能。STM32作为一种强大的微控制器,广泛应用于3D打印机的控制系统中。本文将介绍如何利用STM32控制3D打印机,并提供优化打印精度的具体方法,包括环境准备、代码示例、常…

基于 MATLAB的混沌序列图像加密算法的研究

一、设计目的及意义 3 二、研究现状 3 三、设计内容 3 四、开发环境 3 五、分析设计 3 1、设计要求 3 2、设计原理 3 3、涉及到的程序代码 ........................................... 4 4、主要思想 6 六、 果及分析 6 1、运行示例 6 2、 果 估 8 七、参考文献 9 八 、 研 究…

了解密钥推导函数KDF-HMAC-SHA-256

引言 在现代密码学中,密钥推导函数(KDF,Key Derivation Functions)扮演着至关重要的角色。它们允许从主密钥或密码生成一个或多个固定长度的密钥,用于各种加密操作。KDF的设计目标是确保从同一主密钥生成的多个密钥在统…

什么是数字签名技术?

信息安全五要素 名称说明机密性机密性是指网络信息不泄露给非授权的用户、实体或程序,能够防止非授权者获取信息完整性完整性是指网络信息或系统未经授权不能进行更改的特性可用性可用性是指合法许可的用户能够及时获取网络信息或服务的特性可控性可控性是指可以控…

clickhouse运维篇(三):生产环境一键生成配置并快速部署ck集群

前提条件:先了解集群搭建流程是什么样,需要改哪些配置,有哪些环境,这个文章目的是简化部署。 clickhouse运维篇(一):docker-compose 快速部署clickhouse集群 clickhouse运维篇(二&am…

Hms?: 1渗透测试

靶机:Hms?: 1 Hms?: 1 ~ VulnHub 攻击机:kail linux 2024 主机扫描阶段发现不了靶机,所以需要按DriftingBlues2一样手动配置网卡 1,将两台虚拟机网络连接都改为NAT模式,并查看靶机的MAC地址 2,攻击机上做主机扫描发现…

论文阅读- --DeepI2P:通过深度分类进行图像到点云配准

目前存在的问题: 单模态配准具有局限性,多模态研究很少跨模态图像到点云配准问题是求解相机坐标系与点云之间的旋转矩阵R ∈ SO(3)和平移向量t ∈ R3。 这个问题很困难,因为由于缺乏点到像素的对应关系,无法使用 ICP、PnP 和捆绑调…

R语言贝叶斯分层、层次(Hierarchical Bayesian)模型房价数据空间分析

原文链接:https://tecdat.cn/?p38077 本文主要探讨了贝叶斯分层模型在分析区域数据方面的应用,以房价数据为例,详细阐述了如何帮助客户利用R进行模型拟合、分析及结果解读,展示了该方法在处理空间相关数据时的灵活性和有效性。&a…

警务辅助人员管理系统小程序ssm+论文源码调试讲解

2系统关键技术 2.1 微信小程序 微信小程序,简称小程序,英文名Mini Program,是一种全新的连接用户与服务的方式,可以快速访问、快速传播,并具有良好的使用体验。 小程序的主要开发语言是JavaScript,它与普…

Webserver(2.7)内存映射

目录 内存映射内存映射相关系统调用内存映射的注意事项如果对mmap的返回值(ptr)做操作,释放内存(munmap)是否能够成功?如果open时O_RDONLY,mmap时prot参数指定PROT_READ | PROT_WRITE会怎样?如果文件偏移量…

c++多线程处理数据

c查询可以调动的线程个数 #include <iostream> #include <thread>int main() {// 查询可调动线程数量std::thread::hardware_concurrency();// 如果函数返回0&#xff0c;表示不支持并发&#xff0c;或者无法确定// 如果返回非0值&#xff0c;表示可以同时激活的线…

51c大模型~合集10

我自己的原文哦~ https://blog.51cto.com/whaosoft/11547799 #Llama 3.1 美国太平洋时间 7 月 23 日&#xff0c;Meta 公司发布了其最新的 AI 模型 Llama 3.1&#xff0c;这是一个里程碑时刻。Llama 3.1 的发布让我们看到了开源 LLM 有与闭源 LLM 一较高下的能力。 Meta 表…

实习冲刺Day12

算法题 爬楼梯 70. 爬楼梯 - 力扣&#xff08;LeetCode&#xff09; 递推写法 class Solution { public:int climbStairs(int n) {int num[50];//开辟一个数组num[1]1,num[2]2;for(int i3;i<n;i){num[i]num[i-1]num[i-2];}return num[n];} }; 递归写法 class Solution…

开源免费的API网关介绍与选型

api网关的主要作用 API网关在现代微服务架构中扮演着至关重要的角色&#xff0c;它作为内外部系统通信的桥梁&#xff0c;不仅简化了服务调用过程&#xff0c;还增强了系统的安全性与可管理性。例如&#xff0c;当企业希望将内部的服务开放给外部合作伙伴使用时&#xff0c;直…

头歌——数据库系统原理(数据的简单查询)

文章目录 第1关&#xff1a;基本 SELECT 查询代码 第2关&#xff1a;带限制条件的查询和表达式查询代码 第3关&#xff1a;使用 WHERE 语句进行检索代码 第1关&#xff1a;基本 SELECT 查询 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 如何获取数据表中指…

推荐一款可视化和检查原始数据的工具:RawDigger

RawDigger是一款强大的工具&#xff0c;旨在可视化和检查相机记录的原始数据。它被称为一种“显微镜”&#xff0c;使用户能够深入分析原始图像数据&#xff0c;而不对其进行任何更改。RawDigger并不是一个原始转换器&#xff0c;而是一个帮助用户查看将由转换器使用的数据的工…

计算机毕业设计Python+大模型股票预测系统 股票推荐系统 股票可视化 股票数据分析 量化交易系统 股票爬虫 股票K线图 大数据毕业设计 AI

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; Python大模型股票预测系统 …

Robot Framwork 介绍与使用

文章目录 Robot Framwork 介绍Robot Framwork的安装安装robotframework-ride建立一个简单的测试用例 Robot Framwork 介绍 Robot Framework是一款python编写的功能自动化测试框架。具备良好的可扩展性&#xff0c;支持关键字驱动&#xff0c;可以同时测试多种类型的客户端或者…

【RabbitMQ】01-RabbitMQ

1. MQ MQ可以有更好的并发性。 2. 安装 docker run \-e RABBITMQ_DEFAULT_USERitheima \-e RABBITMQ_DEFAULT_PASS123321 \-v mq-plugins:/plugins \--name mq \--hostname mq \-p 15672:15672 \-p 5672:5672 \--network hm-net\-d \rabbitmq:3.8-management3. 结构 4. 数据…

最新PHP校园源码系统开发(多客社区校园系统源码全套APP源码附搭建教程)

最新PHP校园源码系统开发、多客社区校园系统源码以及全套APP源码附搭建教程的需求 一、最新PHP校园源码系统开发 技术栈选择&#xff1a; 后端&#xff1a;PHP&#xff08;建议使用PHP 7.2或更高版本&#xff09;数据库&#xff1a;MySQL&#xff08;建议使用MySQL 5.6或更高版…