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]]=>“报错原因”
- 方法不报错:
- 不论执行的是基于p1.then存放的 onfullfilledCallback / onrejectedCallback两个方法中的哪一个
- 如果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;
});
图形解析: