Promise的底层详解、应用及扩展

PROMISE

PROMISE:承若模式,主要用来规划异步编程的。是ES6 新增加的内置类,代表了一个异步操作的最终完成或者失败。
在这里插入图片描述
创建一个Promise实例:let p = new Promise([executor])

[executor]: 可执行函数

  • [executor] : 可执行函数
    • new Promise的时候,在Promise内部会立即把[executor]函数执行
    • 函数中一般用来管理一个异步编程代码(可以不管控)
    • 同时给函数[executor]传递两个值(函数类型值):resolve / reject
  • p 是Promise类的实例对象
    • 内置私有属性
      • [[PromiseState]]实例状态:pending(准备态) 、fullfilled / resolved(成功态)、rejected(失败态)
      • [[PromiseResult]]实例的值 :状态一旦被修改,则不会被二次修改。
    • 公共属性方法:
      • catch:
      • finally:
      • then:
      • Symbol(Symbol.toStringTag):“Promise”【Object.prototype.toString.call()中判断数据类型的依据Symbol(Symbol.toStringTag)
  • [executor]函数立即执行,实例状态改变
    • resolve(“OK”) : [[PromiseState]] => fullfilled   [[PromiseResult]]=>“OK”
    • reject(“NO”) : [[PromiseState]] => rejected  [[PromiseResult]]=>“NO”
    • [executor]函数报错:[[PromiseState]] => rejected  [[PromiseResult]]=>“报错原因”【Promise内部做了异常捕获( try / catch)】

then

  • 实例状态的改变,可以控制then方法执行时,存放的两个方法中的某一个方法执行
    • 状态成功时执行 onfullfilledCallback ,并且把 [[PromiseResult]]值传递给方法
    • 状态失败时执行 onrejectedCallback,并且把 [[PromiseResult]]值传递给方法
  • 执行p.then(onfullfilledCallback, onrejectedCallback)
    • 首先把传递进来的 onfullfilledCallback和onrejectedCallback存储起来【存储在一个容器中:可以基于then给其存储多个回调函数】
    • 再去验证当前实例的状态
      • 如果实例状态是pending,则不做任何处里
      • 如果实例状态已经变成 fullfilled 或者rejected,则通知对应的回调函数执行【不会立即执行,而是存放在eventQueue中的微任务队列中】
  • Promise本身是同步的,是用来管理异步的,但是then方法是异步的微任务
  • 执行then方法返回一个全新的Promise实例p2,p2的状态和值的改变情况如下:
    • 不论执行的是基于p1.then存放的 onfullfilledCallback / onrejectedCallback两个方法中的哪一个
      • 方法不报错:
        • 如果方法中返回一个全新的promise实例,则这个“全新的promise实例”的成败决定p2的成功和失败
        • 如果方法中返回的不是Promise实例,则[[PromiseState]] => fullfilled ; [[PromiseResult]]=>“返回值”
      • 方法报错:p2的[[PromiseState]] => rejected; [[PromiseResult]]=>“报错原因”
  • 如果onfullfilledCallback/ onrejectedCallback不传递,则状态和结果都会顺延[/穿透]到下一个同等状态应该执行的回调函数上【内部设置了一些实现效果的默认函数】

成功的顺延

Promise.resolve(10).then(null /!* result=>{ return result; } *!/ , reason => {
    console.log(`失败了 -> ${reason}`);
    return reason / 10;
}).then(result => {
    console.log(`成功了 -> ${result}`); //成功->10
    return result * 10;
}, reason => {
    console.log(`失败了 -> ${reason}`);
    return reason / 10;
}); 

失败顺延

Promise.reject(10).then(null, null /!* reason=>{ return Promise.reject(reason); } *!/ ).then(result => {
    console.log(`成功了 -> ${result}`);
    return result * 10;
}, reason => {
    console.log(`失败了 -> ${reason}`);
    return reason / 10;
});

DEMO:

let  p = new Promise((resolve, reject)=>{
	console.log(1);
	resolve("OK");//同步修改其状态和结果
	console.log(2);
});
console.log(p) //=>Promise{<fullfilled>:"OK"} 此时状态已经修改成功

//------------------
let p = new Promise((resolve, reject)=>{
	console.log(1);
	setTimeout((resolve, reject)=>{
		//异步宏任务
		//改变实例的状态和值(同步)
		// 通知之前基于then存放的onfullfilledCallback执行【异步微任务:也是把方法执行的事情放置在eventQueue中的微任务队列中】
		resolve("OK");
	},1000)
	console.log(2);	
});
let p2 = p.then(result =>{//此时接收onfullfilledCallback的时候,状态是 pending,只是把方法存放起来,不做任何处理
	console.log('result-->',result);
	// return Promise.reject("xxx"); //可以返回一个全新的promise实例,其成败决定p2的成功和失败
});
console.log(3);
// d等1000ms之后,执行定时器中的函数【把异步宏任务拿出来执行】
// 1,
// 2,
// 3
// 1000ms之后 报错:"resolve is not a function"

catch

真实项目中:then只是处理成功的 catch处理失败(一般写在最后)

  • 返回失败状态的实例,但是没有做失败的处理,浏览器控制台有报错(但是不会影响其他代码执行)
  • 再最后加catch解决这个问题
Promise.reject(10).then(result => {
    console.log(`成功了 -> ${result}`);
    return result * 10;
}).catch(reason => {});
setTimeout(() => {
    console.log('OK');
}, 1000);

// ----------
new Promise((resolve, reject)=>{
	resolve("OK");
}).then(result=>{
	console.log("成功-->",result)
}).then(result=>{
	console.log("成功-->",result)
	return Promise.reject("xxx");
}).catch(reson=>{
	console.log("失败-->",reson)
});
//"成功-->OK"
//"成功-->undefined"
//"失败-->xxx"

面试题:

new Promise(resolve => {
	//同步修改状态和值
  console.log("promise1"); 
    resolve();
}).then(() => {
    console.log("then1 ");
    new Promise(resolve => {
        console.log("promise2");
        resolve();
    }).then(() => {
        console.log("then21")
    }).then(() => {
        console.log("then22")
    })
}).then(() => {
    console.log("then12");
})

在这里插入图片描述
* 如果任务队列中有多个任务,谁先到达可执行条件谁先执行。
拓展:

new Promise(resolve => {
	//同步修改状态和值
  console.log("promise1"); 
    resolve();
}).then(() => {
    console.log("then1 ");
    return new Promise(resolve => {
        console.log("promise2");
        resolve();
    }).then(() => {
        console.log("then21")
    }).then(() => {
        console.log("then22")
    })
}).then(() => {
    console.log("then12");
})

在这里插入图片描述

promise的其他方法

iterable:Array,Map,Set都属于ES6的iterable类型

  • promise.all(promise的iterable类型):【可迭代对象】
    • 只返回一个新Promise实例,返回的新Promise实例成败取决于传入的promise的iterable类型中每一个promise实例的成败,
    • 只要有一个失败或者报错,返回的新Promise实例promise实例就是失败,结果就是当前实例失败的原因
    • 当且仅当传入的可迭代对象为空时为同步
  • promise.race(iterable):方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

Promise 解决回调地狱问题

回调函数 : 把一个函数作为值,传递给另一个函数,在另一个函数执行的时候,再把传递进来的函数进行处理。
回调地狱 : 回调函数嵌套回调函数(传统异步编程会导致回调地狱)
常见的就是$.Ajax中串行出现的回调地狱

需求:基于JQ中提供的$.ajax从服务器获取数据(异步编程)
‘/api/info’ 获取个人信息 {id:xxx,name:xxx,…}
‘/api/score?id=xxx’ 根据个人ID获取他的分数信息 {chinese:98,math:99,…}
‘/api/paiming?val=98’ 根据分数,获取他在全年级的排名 {pai:2}
三个请求相互之间是有依赖的,上一个请求完成,才能执行下一个请求 “AJAX串行”

方法一:回调地狱

var data = null
$.ajax({
	url:"/api/info"
	success:function(result){
	//数据获取成功之后执行回调函数
	//result就是从服务器获取的结果
		data = result;
		$.ajax({
			url:`/api/score?id=${data.id}`
			success:function(result){
				$.ajax({
					url:`/api/paiming?val=${result.chinese}`
					success:function(result){
						//...
					}
				});
			};
		});
	};
});

方法二:基于Promise实现解决回调地狱

//获取个人信息
function queryInfo(){
	return new Promise(resolve => {
		$.ajax({
			url:`/api/info`
			success:function(result){
				resolve(result);
			}
		});
	})
}

//获取分数
function queryScore(id){
	return new Promise(resolve => {
		$.ajax({
			url:`/api/score?id=${id}`
			success:function(result){
				resolve(result);
			}
		});
	})
};
//获取排名
function queryPaiMing(ival){
	return new Promise(resolve => {
		$.ajax({
			url:`/api/paiming?val=${val}`
			success:function(result){
				resolve(result);
			}
		});
	})
};

queryInfo().then(result =>{
	return queryScore(result.id)
}).then(result =>{
	return queryPaiMing(result.chinese)
}).then(result => {
//result :获取的排名信息
})

方法三:async await

//获取个人信息
// ……

//获取分数
// ……
//获取排名
// ……
//ECMAScript 2017 标准的 async/await 语法糖中,这种异步代码的对称性得到了极致的体现
(async function(){
	const result = await queryInfo();
	const newResult = await queryScore(result.id);
	const finalResult = await queryPaiMing(result.chinese);
	//finalResult:获取的排名信息
})()

promise函数执行异步性图形解析

JS是单线程的:大部分代码都是同步代码,少数代码是异步的:(宏任务 / 微任务)

  • 宏任务:
    • 定时器
    • 事件绑定
    • AJAX:一般情况下是异步的
  • 微任务:
    • Promise
    • async await
    • generator
    • requestAnimationFrame
  • 微任务优先级高于宏任务

EG1:

let p1 = new Promise((resolve, reject) => {
    resolve('OK');
});
p1.then(result => {
    console.log(`成功了 -> ${result}`);
}, reason => {
    console.log(`失败了 -> ${reason}`);
});
console.log('哈哈哈');
// "哈哈哈" 
// 成功了 -> "OK"

图形解析:
在这里插入图片描述
EG2:

new Promise((resolve, reject) => {
    // resolve(10);
    reject(20);
}).then(result => {
    console.log(`成功了 -> ${result}`);
    return result * 10;
}, reason => {
    console.log(`失败了 -> ${reason}`);
    return reason / 10;
}).then(result => {
    console.log(`成功了 -> ${result}`);
    return Promise.reject(result * 10);
}, reason => {
    console.log(`失败了 -> ${reason}`);
    return reason / 10;
}).then(result => {
    console.log(`成功了 -> ${result}`);
    return result * 10;
}, reason => {
    console.log(`失败了 -> ${reason}`);
    return reason / 10;
});

图形解析:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值