期约(Promise)与异步函数

本文深入浅出地讲解了Promise的基本概念,包括其在同步和异步操作中的作用,期约的定义、then方法的使用,状态转换(pending, resolve, reject)以及链式操作、catch和Promise.all()、Promise.race()方法的应用。适合理解和实践异步编程的开发者。
摘要由CSDN通过智能技术生成

文章目录


前言

学习期约之前,我们先来简单的了解一下同步与异步

同步行为和异步行为是计算机科学的一个基本理念,可以理解为程序执行的两种模式


一、Promise是什么?

Promise是ES6新增的一个内置类(不兼容IE浏览器),我们叫他期约、或者约定。和人类语言相似,意思是对某个现阶段不存在的东西进行约定操作

Promise是一种设计模式(承诺者模式)

  • 该设计模式的组成立承诺  ==》根据承诺执行动作
  • 要干什么事情、状态(成功,失败)、成功/失败后对应要做的事情。后续需要做的事情,按照之前的承诺去履行就行了。

Promise是为了管理“异步编程”代码,解决异步编程中回调的问题

二、同步和异步

1、同步行为

  • 我们编写的每句代码,解析以后其实对应的就是一条条发送给系统的指令,在同步行为下,每条指令都会严格按照它们出现的顺序来执行,而
  • 在同步行为下,程序执行的每一步都可以推断出程序的状态,这是因为后面的指令总是在前面的指令完成后才会执行。同步代码可以轻易的分析出程序在执行到任意代码位置时的状态。

下面举一个简单的例子: 

            fun()
			console.log(2)
			function fun() {console.log(1)}
			console.log(3)
           //控制台输出 1 2 3

由以上同步代码我们可以看出来,每条指令都会严格按照它们出现的顺序来执行

2、异步行为

  • 异步行为类似于系统中断,即当前进程外部的实体可以触发代码执行。异步操作经常是必要的,因为强制进程等待一个长时间的操作通常是不可行的(同步操作则必须要等)。如果代码要访问一些高延迟的资源,比如向远程服务器发送请求并等待响应,那么就会出现长时间的等待,所以我们的请求都会封装成异步操作。
  • 相反的,我们可以将 异步看成是 不同步伐的执行代码,且异步代码是在不同的跑道,那么指令的执行顺序当然是不可预知的。

我们看一个简单的例子

   let x = 10; 
   setTimeout(() => x = x + 10, 1000);

 在上面这段代码中,第一排和第二排是同步代码,但是第二排里面的箭头函数是异步代码,也可以说是一个回调的过程,在1秒中以后才将该函数推入事件流执行的队列中去。

这涉及到了JavaScript的运行机制:单线程事件循环(Event Loop).

  • 就是先处理主模块(主线程)上的同步任务, 再处理异步任务. 异步任务使用事件循环机制完成调度。通俗的来讲就是,JS代码会先循环执行,第一遍会先执行同步代码,如果解析到异步代码会将异步代码放到下一轮。等到同步代码都解析完毕才会解析异步代码

下面看一个例子来验证 

let x = 10;
let y = 20;
setTimeout(() => {
    x = x + 1
	console.log(x)//11
});
	console.log(x);//10
	console.log(y)//20
//输出 10 20 11

如果按照正常思路来理解  上面代码中的setTimeout方法并没有设置延迟执行,是不是就应该立刻执行,然后执行里面的输出语句,然后再执行下面两个输出语句得到11,11, 20。

但是JavaScript 是单线程的,所以每次只能执行一段代码。为了调度不同代码的执行,JavaScript 维护了一个任务队列。其中的任务会按照添加到队列的先后顺序执行setTimeout()的第二个参数只是告诉 JavaScript 引擎在指定的毫秒数过后把任务添加到这个队列。如果队列是空的,则会立即执行该代码。如果队列不是空的,则代码必须等待前面的任务执行完才能执行。


期约

1、期约的定义、使用

实现步骤

  • 设立一个承诺:也就是创建一个Promise实例
  • 在Promise实例中,传递的参数必须是一个函数,否则将会报错(该函数可以是一个执行函数,一般是一段用于管理异步编程的代码)
  • 在"new Promise"使,会将传递进去的函数立即执行
  • 该函数中要有两个形参(resolve,reject),这两个参数的参数类型是新的函数
  • 为了在需要的时候才去创建Promise,我们一般将创建Promise对象的代码封装在一个函数中
 function newPro() {
	 return new Promise(() => {
	 setTimeout(() => {
		console.log("Promise内置对象")
	});
})
}
	newPro()

2、then方法

  •   如何保证匿名内部函数的代码运行完后再执行后面的代码?这时候就需要调用Promise里面      的then()方法(then就是然后的意思)
  • then()是为期约实例添加处理程序的主要方法

   

function newPro() {     //首先通过方法封装一个期约对象
  return new Promise((resolve, reject) => {
	setTimeout(() => {
	console.log("Promise内置对象")
		resolve();
	}, 2000);
})
	}
newPro().then(() => {      //再通过对象调用then方法
	console.log("这段代码会在resolve方法执行完后再执行")
})
console.log("这段代码以同步代码的形式执行")

      在上段代码中我们可以看到then方法是接收了两个参数的 ,这两个参数实际上是针对期约的结果执行的处理程序 。分别是  onResolved处理程序和onRejected处理程序 

  • 这两个参数都是可选的,如果提供的话,则会在期约分别进入“”成功和失败“”状态时执行
  • 当Promise期约返回的结果为resolve(成功)时,执行then的onResolve处理程序(即then的第一个参数)
  • 当Promise期约返回的结果为reject(失败)时,执行then的onRejected处理程序(即then的第二个参数)
// 1、定义两个期约实例:
var pro1 = new Promise((resolve, reject) => {
	resolve()
        /给期约一个允许的状态
})
var pro2 = new Promise((resolve, reject) => {
	reject()
    //给期约一个拒绝的状态
})
// 2、定义不同期约状态的执行程序事件
function onResolved() {
console.log("期约允许")
}
function onRejected() {
console.log("期约拒绝")
			}
// 3、调用then方法,根据期约的状态执行不同的处理事件
// pro1.then()方法输出期约允许
// pro2.then()方法输出期约拒绝
pro1.then(() => {
	onResolved();//期约成功调用onResolved()方法
	}, () => {
	onRejected();
})

pro2.then(() => {
	onResolved();
	}, () => {
	onRejected();//期约拒绝调用onRejected()方法
})

3、期约的三种状态、参数传递

  1. pending:待定
  2. resolve/fulfilled:兑换
  3. reject:拒绝

 【注】:这里的兑现、拒绝并不是指程序上的成功或失败(报错),而只是程序逻辑上的成功或失败。(比如需要返回一个数值1,返回1就成功resolve,返回2则失败reject)

例如:

function newPro(num) {
	return new Promise((resolve, reject) => {
	if (num > 0) {
		resolve("成功")
} else {
	    reject("失败")
      }
  })
 }
var a = newPro(2)
console.log(a)   //成功
var b = newPro(0)
console.log(b)   //失败

期约状态与then的联用:

当期约实例中的状态函数被调用时,还会触发then方法的调用,并且还可以给then中的方法传递参数使用

function newPro() {
   return new Promise((resolve, reject) => {
	  if (num > 5) {     //如果随机数num大于5则期约成功
		resolve(num)
			} else {
			reject(num)//否则失败
		      }
		})
	}
var num = Math.floor(Math.random() * 10);  
newPro().then((num) => {
	console.log(`数值大于5:${num}`)  //当期约成功时,输出随机数的值
	}, (num) => {
	console.log(`数值不大于5:${num}`) //当期约失败时,输出随机数的值
})

4、链式操作

  1. 通过链式操作,我们可以连续的对期约求值,按顺序的进行异步操作。这里同样要用到then方法、
  2. 例如我们在购物软件购买商品时,一般需要经过以下三个步骤:选择商品、下单购买、收取快递。不管单独动作执行的时间是多少,但这一系列的动作一定是有先后顺序的。
//定义 “选择商品” 的期约实例
 function tack1() {
	return new Promise(function(resolve, reject) {
	setTimeout(function() {
	// console.log("选择商品")
		resolve("耐克");
			}, 2000)
	});
}; 
 //定义“下单”的期约实例
function tack2() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
	// console.log("下单购买")
		resolve('付款999');
	// reject("拒绝付款")
		}, 2000)
	});
};
 //定义“收取快递”的期约实例 
function tack3() {
   return new Promise(function(resolve, reject) {
  	setTimeout(function() {
		// console.log("收快递")
			resolve("张三");
		}, 2000)
	})
}
 //通过链式操作,使异步代码有序的执行
	tack1().then(function(data) {
	console.log("你选择了商品")
	console.log(data)
	return tack2();
	}).then(function(data) {
	console.log("你支付了")
	console.log(data)
	return tack3();
	}).then(function(data) {
	console.log("快递信息为:")
	console.log(data)
	})

这里我们可以看到,我们使用链式操作将几个异步代码按我们需要的顺序执行


 5、方法

catch()方法

  • 与try....catch语法类似,Promise对象的catch方法也是用来捕获异常的。
  • 这里的catch方法更多的是使用在链式操作上。我们知道,异步代码的报错是不会影响到第一轮同步代码的。但是,当我们通过链式语法操作多个异步代码是,那么前一个期约报错,后面的期约将会停止运行。但是我们可以使用catch方法来捕获异常
let newPro = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve(9);
		}, 2000)
	})
newPro.then((num)=>{
	console.log(`成功获取数值${num2}`)//这边找不到num2代码不会报错,会在
	}).catch((num)=>{
	console.log(`未找到num2`)
	return newPro;
	}).then((num)=>{
	console.log(`成功获取数值${num}`)
	return newPro;
})

如上面代码所示:在一节链式操作执行完以后,可以为该段链式进行异常捕获,在后面调用 catch() 方法即可。同样,catch方法也接受一个内置的匿名方法,作为报错后执行的操作。


Promise类提供两将多个期约实例组合成一个期约的静态方法:Promise.all()和Promise.race()。而期约合成后的行为取决于内部期约的具体行为

all()方法

  • Promise.all()静态方法创建的期约会在一组期约全部解决之后再解决。这个静态方法接收一个可迭代的对象,返回一个新的期约
  • 通过all创建的合成期约,必须等到所有的内部期约都执行成功后,才会执行。如果所有期约都成功解决,则合成期约的解决值就是所有内部期约解决值的数组,且按照迭代器顺序返回
  • 如果期约拒绝,则第一个拒绝的期约会将自己的理由作为合成期约的拒绝理由(返回值),之后再拒绝的期约不会影响最终期约的拒绝理由。(也就是只有第一个出现拒绝的期约,才会将拒绝理由展示到外部,其余的拒绝理由不会展示),但是其他内部拒绝理由任然会执行,只不过不展示到外部

race()方法

  • Promise.race()静态方法返回一个包装期约,是一组集合中最先解决或拒绝的期约的镜像。这个方法接收一个可迭代对象,返回一个新期约
  • 该方法返回的新期约,只会对第一个出现反应的内部期约有效果(无论状态)。同样的,奇特内部期约还是会执行,只不过不会影响到外部期约。
  • 与all方法相反,race方法只要有一个内部版期约有结果,那么外部新期约就会执行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值