异步处理Promise

事件循环

JS运行的环境成为宿主环境(JS可以在多种环境下运行,不止只在浏览器环境下运行)
执行栈:call stack,一个数据结构,用于存放各种函数的执行环境,每一个函数执行前,他的相关信息会加入到执行栈。函数调用前,创建执行环境,然后加入到执行栈,函数调用后,销毁执行环境。

注:JS引擎永远执行的是执行栈的最顶部

异步函数:某些函数不会立即执行,需要等到某个时机才会执行,这样的函数称为异步函数。异步函数的执行时机,会被宿主环境控制

浏览器宿主环境中的五个线程:

JS引擎负责执行执行栈最顶部代码
GUI线程负责渲染页面
事件监听线程负责监听各种事件
计时线程负责计时
网络线程负责网络通信

当上面线程发生某些事,这些事需要处理程序,它会将该处理程序加入事件队列(event queue),当JS引擎发现,执行栈中已经没有任何内容后,会将事件队列中第一个函数加入到执行栈中执行。

JS引擎对事件队列的取出方式,以及宿主环境的配合,称之为事件循环(event loop)

事件队列在不同的宿主环境中有所差异,大部分宿主环境会将事件队列进行细分,在浏览器中,事件队列分为两种:

  • 宏任务:计时器结束的回调,事件回调,HTTP回调等等绝大部分异步函数进入宏队列
  • 微任务:Promise产生的回调进入微队列
    当执行栈清空时,JS引擎首先会将微任务中所有任务依次执行结束,如果没有微任务,则执行宏任务。

事件和回调函数的缺陷

事件:某个对象的属性是一个函数,当发生某一事件时,运行该函数
回调函数:运行某个函数以实现某个功能时,传入一个函数作为参数,当发生某件事时,会运行该函数
本质上,事件与回调没有区别,只是函数放置位置不同,随着前端工程越来越复杂,该模式面临一些问题。

  1. 回调地狱:某个异步操作需要等待之前的异步操作完成,无论是回调还是事件,都会陷入不断地嵌套。
  2. 异步之间的联系:某个异步操作需要等待多个异步操作完成,对这种联系的处理,会让代码的复杂度骤增。

异步处理的通用模型

ES官方参考大量的异步场景,总结出一套通用模型,该模型可以覆盖几乎所有的异步场景,甚至同步场景。基于该模型推出新的API

理解该异步模型:
ES6将一件可能发生异步操作的事情,分为两个阶段:unsettlesettled

  • unsettle:未决阶段,表示事情还在进行前期处理,并没有发生通向结果的那件事
  • settled:已决阶段,事情已经有了一个结果,不管结果结果如何,整件事情无法逆转
    在这里插入图片描述

事情总是从未决阶段逐步发展到已决阶段,并且未决阶段可以控制何时通向已决阶段的能力

ES6将事情分为三个状态:pending、resolved、rejected

  • pending:挂起状态,处于未决阶段,表示事情还在挂起(最终的结果还没有出来)
  • resolved:已处理,已决阶段的一种状态,表示整件事情已将出现结果,并且是一种可以按照正常逻辑进行下去的结果
  • rejected:已拒绝,已决阶段的一种状态去,表示整件事情已经出现结果,并且是一个无法按照正常逻辑进行下去的结果,通常表示一种错误

未决阶段有能力决定事情走向,因此未决阶段可以决定事情最终的状态,我们将把事情变为resolved状态的过程叫做:resolve,推向该状态时,可能会传递一些数据,我们将把事情变为rejected状态的过程叫做:reject,推向该状态时,同样可能会传递一洗数据,数据通常为错误信息
注:无论是阶段还是状态,都是不可逆的
在这里插入图片描述
事情达到已决阶段后,通常需要进行后续处理,不同的状态决定了不同的后续处理

  • resolved状态:正常的已决状态,后续处理表示为thenable
  • rejected状态:非正常的已决状态,后续处理表示为catchable

后续处理可能有多个,因此会形成作业队列,这些后续处理会按照顺序,当状态到达后依次进行
整件事成为Promise
在这里插入图片描述

Promise基本使用

const pro = new Promise((resolve, reject)=>{
    // 未决阶段的处理
    // 通过调用resolve函数将Promise推向已决阶段的resolved状态
    // 通过调用reject函数将Promise推向已决阶段的rejected状态
    // resolve和reject均可以传递最多一个参数,表示推向状态的数据
})
pro.then(data=>{
    //这是thenable函数,如果当前的Promise已经是resolved状态,该函数会立即执行
    //如果当前是未决阶段,则会加入到作业队列,等待到达resolved状态后执行
    //data为状态数据
}, err=>{
    //这是catchable函数,如果当前的Promise已经是rejected状态,该函数会立即执行
    //如果当前是未决阶段,则会加入到作业队列,等待到达rejected状态后执行
    //err为状态数据
})

注:

  1. 未决阶段的处理函数是同步的,代码会立即执行
  2. thenable和catchable函数是异步的,就算是立即执行也会先加入事件队列中等待执行,并且,加入的队列为微队列。
  3. pro.then可以只添加thenable函数,pro.catch可以单独添加catchable函数
  4. 在未决阶段的处理函数中,如果发生未捕获的错误,会将状态推向rejected,并会被catchable捕获
  5. 一旦状态推向了已决阶段,无法再对状态做任何更改
  6. Promise并没有消除回调,只是让回调变得可控

promise的串联

当后续的Promise需要用到之前的Promise的处理结果时,需要Promise的串联
Promise对象中,无论是then方法还是catch方法,它们都具有返回值,返回的是一个全新的Promise对象,它的状态满足下面的规则:

  • 如果当前的Promise是未决的,得到的新的Promise是挂起状态
  • 如果当前的Promise是已决的,会运行响应的后续处理函数,并将后续处理函数的结果(返回值)作为resolved状态数据,应用到新的Promise中;如果后续处理函数发生错误,则把返回值作为rejected状态数据,应用到新的Promise中。

后续的Promise一定会等到前面的Promise有了后续处理结果后,才会变成已决状态

const pro1 = new Promise((resolve, reject) => {
	resolve(1)
	// throw 1;
})
const pro2 = pro1.then(
	data => {return data * 1},
	err => {
		return err * 2;
		// throw 2;
	}
)
pro2.then(
	data => {return console.log(data * 3)},
	err => {return console.log(err * 2)}
)
console.log(pro2);	// pro2是一个Promise对象,此时pro2处于pending状态
// 最后的输出结果为 3
// 如果pro1运行出现错误,最后的结果为 6
// 如果pro1、pro2运行都出现错误,最后的结果为 4

如果前面的Promise的后续处理,返回的是一个Promise,则返回的新的Promise状态和后续处理返回的Promise状态保持一致。

Promise的其他API

原型成员(实例成员)

  • then:注册一个后续处理函数,当Promise为resolved状态时运行该函数
  • catch:注册一个后续处理函数,当Promise为rejected状态时运行该函数
  • finally:[ES2018]注册一个后续处理函数(无参),当Promise为已决时运行该函数

构造函数成员 (静态成员)

  • resolve(数据):该方法返回一个resolved状态的Promise,传递的数据作为状态数据
const pro = new Promise((resolve, reject) => { resolve(1) })
// 等效于
const pro = Promise.resolve(1);

特殊情况:如果传递的数据是Promise,则直接返回传递的Promise对象

const p = new Promise((resolve, reject) => { resolve(1) })
const pro = Promise.resolve(p);
console.log(pro === p);		// true
  • reject(数据):该方法返回一个rejected状态的Promise,传递的数据作为状态数据
const pro = new Promise((resolve, reject) => { reject(1) })
// 等效于
const pro = Promise.reject(1);
  • all(iterable):这个方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。这个新的promise对象在触发成功状态以后,会把一个包含iterable里所有promise返回值的数组作为成功回调的返回值,顺序跟iterable的顺序保持一致;如果这个新的promise对象触发了失败状态,它会把iterable里第一个触发失败的promise对象的错误信息作为它的失败错误信息。Promise.all方法常被用于处理多个promise对象的状态集合。
function getRandom(min, max) {
	return Math.floor(Math.random() * (max - min)) + min;
}
const pros = {};
for( let i=0; i<10; i++) {
	pros.push(new Promise((reslove, reject) => {
		setTimeout(() => {
			console.log(i, "完成");
			resolve(i);
		}, getRandom(1000, 5000));
	}))
}
// 等所有的Promise变成resolved状态后输出:全部完成
const pro = Promise.all(pros);
pro.then(datas => {
	console.log("全部完成", datas);	// "全部完成" [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
})
console.log(pros);	// [Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise, Promise]
	
  • race(iterable):当iterable参数里的任意一个子promise被成功或失败后,父promise马上也会用子promise的成功返回值或失败详情作为参数调用父promise绑定的相应句柄,并返回该promise对象

async和await

async 和 await 是 ES2016 新增两个关键字,它们借鉴了 ES2015 中生成器在实际开发中的应用,目的是简化 Promise api 的使用,并非是替代 Promise。

async

async:目的简化在函数的返回值中对Promise的创建,用于修饰函数(无论是函数字面量还是函数表达式),放置在函数最开始的位置,被修饰函数的返回结果一定是Promise对象

async function test() {
	console.log(1);
	return 2;
	// throw 3;
}

// 等效于:

function test() {
	return new Promise((resolve, reject) => {
		console.log(1);
		resolve(2);
		// reject(3);
	})
}

await

await关键字必须出现在async修饰的函数中
await:用在某个表达式之前,如果表达式是一个Promise,则得到的是thenable中的状态数据。

async function test1(){
    console.log(1);
    return 2;
}
async function test2(){
    const result = await test1();
    console.log(result);
}
test2();

// 等效于

function test1(){
    return new Promise((resolve, reject)=>{
        console.log(1);
        resolve(2);
    })
}
function test2(){
    return new Promise((resolve, reject)=>{
        test1().then(data => {
            const result = data;
            console.log(result);
            resolve();
        })
    })
}
test2();

如果await的表达式不是Promise,则会将其使用Promise.resolve包装后按照规则运行

async function test() {
	const result = await 1;
	console.log(result);
}

// 等效于:

function test() {
	return new Promise((resolve, reject) => {
		Promise.resolve(1).then(data => {
			const result = data;
			console.log(result);
			resolve();
		})
	})
}

用await得到一个错误的结果

async function getPromise() {
	if(Math.random() > 0.5) {
		return 1;
	} else {
		throw 2;
	}
}
async function test() {
	try{
		const result = await getPromise();
		console.log("正常状态", result);
	} catch (err) {
		console.log("错误状态", err);
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值