又是一个...标题党?
不不不,我是来给你的肚子填食儿的,相信我 🤩🤩🤩
Promise 基础语法
Promise
ES6
中新增一个内置的类:Promise
承诺/约定模式, 基于这种模式可以有效的处理异步编程- 异步:上面的事情没有完成也不会等,下面的事情继续处理
- 同步:上面事情没处理完之前,下面事情是无法去处理的
回调地狱
需求:首先从服务器端基于/api/1
这个接口获取数据;紧接着把获取的某些数据作为参数,在基于api/2
获取其他的数据;最后在第二次数据获取成功后,在基于api/3
获取新的数据…也就是第一个请求成功才可以获取第二个,第二个成功才可以获取第三个…(AJAX
的串行)
let data = null;
// 基于 AJAX 异步从服务器获取数据(真实项目中从服务器获取数据也都是异步的)
$.ajax({
url: '/api/1.json',
method: 'GET',
success: function(result) {
// result 就是基于 JQ 的 ajax 方法从服务器获取的结果
console.log(result);// 后输出 第二次输出[1]
data = result;
}
});
console.log(data);// 先输出 第一次输出 null
回调地狱
:传统在实现异步操作,并且是串行的模式下,基本上都是回调函数嵌套回调函数,实现回调地狱的问题
$.ajax({
url: '/api/1.json',
method: 'GET',
success: function(result) {
// result 就是基于 JQ 的 ajax 方法从服务器获取的结果
console.log(result);// 后输出 第二次输出[1]
data = result;
// ajax 串行:第一个请求成功才可以发送下一个请求
$.ajax({
url: '/api/1.json',
method: 'GET',
success: function(result) {
console.log(‘第二次请求’+result);
}
});
}
});
此时我们需要一种优秀的代码管理模式,能够有效的管理异步编程中的代码,通过这种代码管理的思想,让代码开发起来更便捷,维护起来也很方便,可读性更强。所以 JS
的设计模式诞生:Promise
设计模式就是管理异步编程的
- 设计模式的优点:按照一定的思想有效管理代码,代码看起来可读性更高,后期可维护性更强。
解决回调地狱问题
- 基于
Promise
解决
const = api1 = ()=>{
return new Promise(resolve=>{
$.ajax({
url: '/api/1.json',
method: 'GET',
dataType: 'json',
success: function(result) {
resolve(result);
}
})
})
};
const = api2 = ()=>{
return new Promise(resolve=>{
$.ajax({
url: '/api/2.json',
method: 'GET',
dataType: 'json',
success: function(result) {
resolve(result);
}
})
})
};
const = api3 = ()=>{
return new Promise(resolve=>{
$.ajax({
url: '/api/3.json',
method: 'GET',
dataType: 'json',
success: function(result) {
resolve(result);
}
})
})
};
// 执行第一个 ajax 请求,执行.then()「.then 方法是控制第一个请求成功」,拿到请求成后的结果传到 resolve(result) 中;
api1().then(result=> {
console.log(‘第一次请求=’, result);
// 发送第二个请求
return api2();
}).then(result=> {
console.log(‘第二次请求=’, result);
// 发送第三个请求
return api3();
}).then(result=> {
console.log(‘第三次请求=’, result);
});
- 终极解决:基于
Promise
管理的async/await
(async function(){
let result = await api1();
console.log(‘第一次请求=’, result);
let result = await api2();
console.log(‘第二次请求=’, result);
let result = await api3();
console.log(‘第三次请求=’, result);
})()
异步编程: 在
JS
中,除了ajax
请求时异步编程的,还有一些操作也是异步编程
- 事件绑定
- 定时器
Promise/async/await
requestAnimationFrame
eg:
// 异步操作
setTimeout(()=>{
console.log(1);// 第二步
}, 1000)
console.log(2);// 第一步
Promise 语法
-
每次
new Promise
的时候,内部要做的事情new Promise
的时候会立即执行传递的executor
函数- 在
executor
函数中一般用来管控一个异步的操作(不写异步的也可以) - 而且传递给
executor
函数两个参数:resolve
/reject
,并且两个参数都是函数
- 在
- 创造
Promise
类的一个实例p1
,每一个Promise
的实例都存在两个属性[[PromiseState]]: promise 状态
:pending
: 准备状态fulfilled
(旧版本浏览器)/resolved
(新版本浏览器): 成功状态(已兑现)rejected
: 失败状态(已拒绝)- 但是一旦状态从
pending
改变为fulfilled
或者是rejected
, 都无法再次改变其状态 - 如果
executor
函数中的代码执行报错,则实例的状态也会变为失败,并且[[PromiseResult]]
就是报错的原因
[[PromiseResult]]: promise 值
:默认是undefined
,一般存储成功的结果或者失败的原因- 每个
Promise
实例指向Promise
类的原型p1.__proto__ = Promise.prototype
Promise
的原型上的方法:then/catch/finally
-
new
的时候必须要传入一个函数才可以,否则报错
let p1 = new Promise();
console.log(p1);// 报错 Promise resolver undefined is not a function at new Promise
let p1 = new Promise(function () {
// 一般写异步操作, 同步也可以
// 同步操作
console.log(1);
// 异步操作
setTimeout(function () {}, 1000)
// 只要让 resolve() 函数执行 Promise 状态就为成功, 值为 undefined
resolve();
// 传值,则 PromiseResult 值为 100
resolve(100);
/*
执行 resolve 控制实例的状态变为成功,传递的值 100,是成功的结果
[[PromiseState]]: 'fufilled'
[[PromiseResult]]: 100
*/
resolve(100);
/*
执行 resolve 控制实例的状态变为成功,传递的值 100,是成功的结果
[[PromiseState]]: 'fufilled'
[[PromiseResult]]: 100
*/
reject(0);
});
console.log(2);
p1.then();
Promsie 是如何管控异步编程的
new Promise
的时候创建一个promise
实例,此时在executor
函数中管理一套异步的代码- 后期等异步操作成功或者失败的时候,执行
resolve/reject
,以此来控制promise
实例的状态和结果 - 根据状态和结果,就可以控制基于
.then
注入的两个方法中的哪一个去执行了
下述代码执行的顺序(下述代码
executor
函数管控的是异步编程)
new Promise
构造函数执行- 执行
executor
函数:设置一个异步定时器 - 执行实例
.then(result, reason)
注入两个方法,注入的方法会保存起来(此时这两个方法还没有执行) - 等待
1000ms
- 执行定时器的回调函数:通过执行
resolve
改变promise
状态和值 - 通知之前基于
.then()
注入的两个方法中的某一个执行
let p1 = new Promise(function (resolve, reject) {
// new Promsie 的时候立即执行 executor 函数,在 executor 函数中管理了一个异步编程代码(此时状态是 pending);当异步操作到达指定时间,开始执行的时候(可以理解为异步操作成功),此时我们通过执行 resolve,把 promise 状态修改为 fulfilled;
setTimeout(function(){
resolve('OK');
}, 1000)
});
p1.then(result=>{
// 当p1 实例的状态修改为 fulfilled 的时候,通知传递的第一个函数执行,result 就是[[PromiseResult]] 的值
console.log('成功', result)
}, reason=>{
// 当p1 实例的状态修改为 rejected 的时候,通知传递的第二个函数执行,reason 就是[[PromiseResult]] 的值
console.log('失败', reason)
});
下述代码管控的顺序(下述代码
executor
函数管控的是同步代码)
new Promise
构造函数执行- 执行
executor
函数:- 输出
1
- 立即修改状态和值,并且通知基于
THEN
注入的方法执行,此时.THEN
方法还没有执行,方法还没有被注入,不知道该通知谁来执行,所以此时需要把通知方法执行的操作先保存起来,放入到等待任务队列中,这个操作本身是异步的,需要等待方法注入完成后再通知其执行 - 输出
2
(到此executor
函数已经执行完成)
- 输出
- 执行实例
.then(result, reason)
注入两个方法,注入的方法会保存起来(此时这两个方法还没有执行) - 输入
3
- 通知之前基于
.then()
注入的两个方法中的第一个执行(result
方法)
let p1 = new Promise((resolve, reject)=>{
console.log('1');// 1
resolve('OK');
console.log('2');
});
p1.then(result=>{
console.log('成功:'+result);
}, reason=>{
console.log('失败:'+reason);
});
console.log('3');
.then
方法是异步操作
let p1 = new Promise(resolve => {
// 1000ms 后,开始执行定时器,此时p1.then()中的两个方法已经注入完成
setTimeout(function(){
// 执行 resolve ,立即修改 promise 状态和值
resolve('OK);
console.log(1);
}, 1000)
});
p1.then(result=>{
console.log(2);
});
// 最终结果为 1 2
-
输出
1 2
- 说明
then
方法为异步:执行resolve
后,不会等待执行then
方法中的result
,而是放在一个事件队列中,先把同步代码执行完成输出1
,在去事件队列中找异步代码去执行输出2
- 说明
-
输出
2 1
- 执行
resolve
后,立即去执行then
方法中的result
输出2
,然后在去执行同步代码 输出1
- 执行
-
结论:不论是否基于
THEN
注入了方法,执行resolve/reject
的时候「修改转态和值」是同步的会立即处理,但是「通知对应注入方法执行」的这个任务是异步操作的,不会立即处理,只是把它排在等待任务队列中,当其他事情处理完,再次返回去,通知对应注入的方法执行
总结
Promise
的三大概念
executor
函数new Promise
的时候会立即执行executor
函数,在executor
函数中管理了一个异步编程代码(同步代码也是OK
的),此时Promise
状态是pending
;在我们异步操作成功或者失败的时候,通过执行resolve
或者reject
函数,可以有效的把实例改成成功状态或者失败状态,也就是从pending
变为fulfilled
(成功)/rejected
(失败);如果代码报错的情况下,也是把状态改为失败;
- 实例
- 每一个
Promise
实例都具备[[PromiseState]]
和[[PromiseResult]]
方法,[[PromiseState]]
是promise
状态:pending
、fulfilled/resolve
、rejected
三种状态;[[PromiseResult]]: promise 值
:默认是undefined
,一般存储成功的结果或者失败的原因。
- 每一个
Promise
原型上的方法.then()
: 用来管控成功或者失败的操作- 实例可以调用
.then()
,它会存放两个方法:result
和reason
(都是函数);- 当
p1
实例的状态修改为fulfilled
的时候,通知传递的第一个函数(result)
执行,result
就是[[PromiseResult]]
的值; - 当
p1
实例的状态修改为rejected
的时候,通知传递的第二个函数(reason)
执行,reason 就是[[PromiseResult]]
的值
- 当
- 不论是否基于
THEN
注入了方法,执行resolve/reject
的时候「修改转态和值」是同步的会立即处理,但是「通知对应注入方法执行」的这个任务是异步操作的,不会立即处理,只是把它排在等待任务队列中,当其他事情处理完,再次返回去,通知对应注入的方法执行
- 实例可以调用