javascript中的糖衣语法--Promise对象

2024-01-23 16:59

本文主要是介绍javascript中的糖衣语法--Promise对象,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

知道Promise对象的你,知道在使用Promise对象的时候,我们如何做到在使用中如鱼得水嘛?10分钟带你迅速上手!

一、Promise的诞生
1、回调地狱

最初javascript的异步实现就是使用回调函数。回调地狱就是:一个函数需要等它的回调函数(或者回调和回调的回调…)执行完毕之后再执行。简单来说,回调函数里面嵌套回调函数。而因为回调地狱的问题,Promise就出现了。我们看看什么是回调地狱:

// 回调地狱
//地狱回调
setTimeout(function () {  //第一层console.log(1);//等4秒打印1,然后执行下一个回调函数setTimeout(function () {  //第二层console.log(2);//等3秒打印2,然后执行下一个回调函数setTimeout(function () {   //第三层console.log(3);//等2秒打印3,然后执行下一个回调函数setTimeout(function () {   //第四层console.log(4);//等1秒打印4}, 1000)}, 2000)}, 3000)
}, 4000) 

可看出回调地狱的特点

1.难以复用
2.堆栈信息被断开
3.借助外层变量 

回调地狱是为了让我们代码执行顺序的一种操作(解决异步),但是它会使我们的可读性非常差。当你有100个,1000个…,代码会不断右移,不够优雅,也会影响性能。嵌套和缩进只是回调地狱的一个梗而已,它导致的问题远非嵌套导致的可读性降低而已。接下里我们今天的目的,就是将上面的回调地狱用Promise解决。

二、Promise的行为
1、Promise的语法

Promise的基本语法:Promise函数接收一个函数作为参数,这个函数有两个参数,一个是成功函数(resolve),一个是失败函数(reject)。Promise的.then接收两个回调函数,一个是成功函数的回调,一个是失败函数的回调。这两个函数可选,不一定要提供

//Promise语法
let myPromise = new Promise(function(resolve, reject) {// "Producing Code"(可能需要一些时间)resolve(); // 成功时reject();  // 出错时});// "Consuming Code" (必须等待一个兑现的承诺)myPromise.then(function(value) { /* 成功时的代码 */ },function(error) { /* 出错时的代码 */ }); 

特别注意:

(1)Promise对象中的状态不会被外界干扰。状态的改变取决于异步的操作结果。

(2)Promise对象的状态一旦被改变,就不会进行再次改变。 例如:

let myPromise = new Promise((resolve, reject) => {setTimeout(() => {resolve('ok');//第一次状态为成功reject('no');//不会改变})
}).then(function (result) { console.log('resolved'); },//成功状态执行then后面的成功回调函数function (error) { console.log('reject'); }
)
//resolved 

(3)Promise新建后就会立即执行,Promise后面的.then是一个异步操作,在事件循环中叫做“微任务”。会放在同步代码后面执行。 例如:

let myPromise=new Promise((resolve,reject)=>{console.log('Promise');//1resolve();
}).then(()=>{//这里是一个异步操作console.log('succeed');//3})
console.log('Promise resolved');//2
// Promise
// Promise resolved
// succeed 
2、Promise的方法
(1)Promise.prototype.then()

then方法的返回结果是新的Promise实例,对象状态由回调函数的执行结果决定。then方法后面还可以再调用另一个then方法,形成链条。采用链式的then,可以指定一组按照次序调用的回调函数。

const p = new Promise((resolve, reject) => {setTimeout(() => {//设置 p 对象的状态为失败,并设置失败的值 reject("出错啦!");}, 1000)
});
p.then(function(value){},function(reason){console.log(reason);}
)
.then(()=>{console.log(123)
}); 
(2)Promise.prototype.catch()

catch()用于指定发生错误时的回调函数.例如:

const p = new Promise((resolve, reject) => {//p为Promise的实例setTimeout(() => {//设置 p 对象的状态为失败,并设置失败的值 reject("出错啦!");//reject()方法的作用,等同于抛出错误}, 1000)
});
// p.then(function(value){},function(reason){
// // console.error(reason); 
// console.log(reason);
// });
​
p.catch(function (reason) {//相当于上面的.then(...)console.log(reason);//捕获reject状态的值
}); 

建议总是使用catch()方法,而不使用then()方法的第二个参数.

catch()还可以这样使用:

const myPromise = new Promise(function(resolve, reject) {throw new Error('出错啦');//从这抛出错误,catch()指定的回调函数也可以捕获});promise.catch(function(error) {console.log(error);
}); 
(3)Promise.prototype.finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。而且finally方法总是会返回原来的值。举个例子:

function a(){return new Promise((resolve,reject)=>{resolve('ok')},1000)
}
​
function b(){return new Promise((resolve,reject)=>{setTimeout(()=>{reject('no')},1500)})
}
function c(){setTimeout(()=>{console.log('finally');},500)}
a().then((res)=>{console.log(res);return b()}).catch((err)=>{console.log(err);}).finally(c)//参数不是回调函数,如果是回调函数也不需要带参
​
//ok
//no
//finally //说明finally()照样执行,有打印 
(4)Promise.resolve()

将现有对象转为 Promise 对象,状态为resolved。举例如下:

let myString='hello';
console.log(myString);//helloconst myPromise=Promise.resolve(myString)//带参
//等同于const myPromise=Promise.resolve('hello')
console.log(myPromise);//Promise { 'hello' }myString=Promise.resolve();//不带参,直接调用
console.log(myString);//Promise { undefined }
myString.then(result=>{//说明myString已经是Promise对象了,只有Promise对象才有.thenconsole.log(result);//undefined
}) 
(5)Promise.reject()

也会返回一个新的 Promise 实例,该实例的状态为rejected。简单举例:

// 以上代码等于
const p=new Promise((resolve,reject)=>{reject('error')
})
p.catch(error=>{console.log(error);//error
})
// 或者
p.then(function(){},function(error){console.log(error);})//error
// 或者
p.then(null,function(error){console.log(error);})//error
// 或者
p.then(undefined,function(error){console.log(error);})//error 
(6)Promise.all()

all()是将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个数组作为参数,数组的每一项都是·Promise对象的实例。如果不是,会通过Promise.resolve()将参数转为Promise实例,再进行处理。all()用于将多个 Promise 实例,包装成一个新的 Promise 实例

// promise.all()
const myPromise1=new Promise((resolve,reject)=>{resolve('sure');
}).then(result=>result)const myPromise2=new Promise((resolve,reject)=>{reject('cancel')
}).then(result=>result).catch(error=>error)//myPromise2有自己的catch
//感兴趣的小伙伴可以尝试,如果删除myPromise2.catch(...)后Promise.all([myPromise1,myPromise2])会如何?Promise.all([myPromise1,myPromise2])//myPromise1,myPromise2都处于成功状态.then(result=>{console.log(result);})//走这里[ 'sure', 'cancel' ].catch(error=>{console.log(error);}) 
(7)Promise.race()

race()是将多个 Promise 实例,包装成一个新的 Promise 实例。接收一个数组作为参数,数组的每一项都是·Promise对象的实例。如果不是,会通过promise.resolve()将参数转为Promise实例,再进行处理。只要参数的Promise实例有一个率先改变状态,则状态改变。例如:

const myPromise2 = new Promise((resolve, reject) => {setTimeout(()=>{reject('cancel') },3000)//如果将时间改为<2000,Promise.race([myPromise1, myPromise2])走哪一步呢?}).then(result => result).catch(error => error)
const myPromise1 = new Promise((resolve, reject) => {setTimeout(()=>{resolve('sure');},2000)//myPromise1比myPromise2更先改变状态}).then(result => result)
​
​
Promise.race([myPromise1, myPromise2]).then(result => { console.log(result); })//走这里,sure.catch(error => { console.log(error); }) 

简要说一下const p1=Promise.all([promise1,promise2,promise3]) 和const p2=Promise.race([promise1,promise2,promise3])的区别

–前者Promise的实例状态都为resolved时,p1调用.then()里面成功时的回调函数;如果实例状态有一个为rejected,p1调用.then()里面失败时的函数或者走.catch()

–后者注重时序,如果率先改变状态的实例为resolved,则p2为reslove状态;否则,为reject状态。

三、Promise的场景
1、Ajax请求
<head><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body> <div class="name"><audio id="audio" controls></audio> </div> <script> function getSong() { return new Promise((resolve, reject) => { $.ajax({url: 'https://www.fastmock.site/mock/c024e8920dd6003c63dcd9ed2bbf6cb9/music/music',dataType: 'json',success(res) {console.log(res);url = res[0].url;}})resolve();})}function playSong() {let audio = document.getElementById('audio');window.addEventListener('click', function () {audio.src = url;audio.play()})}getSong().then(playSong()) </script>
</body> 
2、文件读取
// 引入fs模块
const fs=require('fs');
// 使用Promise封装
const P=new Promise(function(resolve,reject){fs.readFile('./text/2.md',(err,data)=>{// 如果地址错误,抛出异常if(err) reject(err) ;// 如果成功,输出内容resolve(data);});
});P.then(function(value){console.log(value.toString());
},function(reason){console.log("defeat!!!!");
}); 
3、图片加载
<body><img src="http://m.imeitou.com/uploads/allimg/220514/5-220514101036.jpg" alt="风景" id="myImage"><script> const preloadImage = function (path) {return new Promise(function (resolve, reject) {const image = new Image();// 图片加载成功image.onload=()=>{resolve(image)}// 图片加载失败image.onerror=()=>{reject('sorry,cannot loding picture')};image.src = path;});};// 获取图片DOM节点var preImage=document.getElementById('myImage')// 图片预加载preloadImage('http://m.imeitou.com/uploads/allimg/220512/5-220512111J0-50.jpg').then(targetImage=>// 点击页面切换图片,让图片加载window.onclick=function(){setTimeout(()=>{ preImage.src=targetImage.src},1000)}) </script>
</body> 
4、函数封装
//例如将微信小程序里的showModal进行Promise封装,那么在任何需要用到此函数的直接引入就很方便了
// promise形式 封装 showModal
export const showModal=({content})=>{return new Promise((resolve,reject)=>{wx.showModal({title:'提示',content:content,success: (result) => {resolve(result);},fail: (err) => {reject(err);}});})
} 
四、Promise的短板

1.无法取消Promise,一旦新建它就会立即执行,无法中途取消

2.如果不设置回调函数,Promise内部抛出的错误,不会反映到外部

3.当处于pending状态时,无法得知目前进展到哪一个阶段,是刚刚开始还是即将完成

总结

恭喜你看到文章末尾了,相信你可以写出Promise方案解决第一个回调地狱的异步问题了,快去试试把!

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

这篇关于javascript中的糖衣语法--Promise对象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/636922

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定