文 / 景朝霞
目录:0 / Promise 的应用(1)ajax串行,很久很久以前的写法(2)Promise应用于异步请求1 / Promise的语法(1)执行resolve函数(2)执行reject函数(3)resolve和reject俩函数都调用了2 / Promise如何管控异步(1)没有宏任务时(2)有宏任务时3 / 小结
0 / Promise 的应用
ES6中新增一个内置的类:Promise承诺/约定模式,基于这种模式可以有效的处理异步编程
△ 图1_promise的兼容性
(1)ajax串行,很久很久以前的写法
需求:
① 首先从服务器端基于"api/A.json"这个接口获取数据,紧接着把获取的某些数据作为参数
② 再基于"api/B.json"获取其他的数据
③ 最后在第二次数据获取成功后,再基于"api/C.json"获取新的数据……
即:A请求成功才可以获取B请求,B成功后才可以获取C请求……【AJAX的串行】
<script src="./node_modules/jquery/dist/jquery.js">script> <script src="./1.js">script>
△ html
//---> /api/A.json { "1":1, "name":"朝霞的光影笔记", "id":"zhaoxiajingjing" } //---> /api/B.json 2 //---> /api/C.json 3
△ 需要请求的数据
如果是这样写的,那么三个ajax之间没有关联:
let data = null; //=> 基于JQ的AJAX"异步"从服务器获取数据 //=> 【项目中从服务器获取数据也是异步的】 $.ajax({ url:'/api/A.json', dataType:'json', success:function(result){ //=> result 是基于JQ的AJAX方法从服务器获取的结果 data = result; console.log('第一次请求',result);//=> 第二次输出:第一次请求 {name: "朝霞的光影笔记", id: "zhaoxiajingjing"} } }); console.log(data);//=>第一次输出:null //=> 【异步:上面的事情没有完,也不会等;下面的事情也会继续执行】 //=> 【同步:上面的事情没有完,后面的事情不会执行】 $.ajax({ url:'/api/B.json', dataType:'json', success:function(result){ console.log('第二次请求', result); } }); $.ajax({ url:'/api/C.json', dataType:'json', success:function(result){ console.log('第三次请求', result); } }); console.log('OK');
△ 三个请求没有关联
而,咱的需求是:三个请求是串行的
△ 图2_晕不晕?绕不绕?
很久很久以前写代码,有时候的需求真得这么干:实现异步操作,并且串行的模式下,基本上都是回调函数嵌套着回调函数,实现非常恐怖的 回调地狱 => 看着头晕,写着头疼
为了不让自己头疼,大佬们就想着怎么治治这帮孩子的头疼呢?
Promise设计模式就是管理异步编程的
(2)Promise应用于异步请求
把三个请求封装成方法:
const apiA = () => { return new Promise(resolve => { $.ajax({ url: '/api/A.json', dataType: 'json', success(result) { resolve(result); } }); }); }; const apiB = () => { return new Promise(resolve => { $.ajax({ url: '/api/B.json', dataType: 'json', success(result) { resolve(result); } }); }); }; const apiC = () => { return new Promise(resolve => { $.ajax({ url: '/api/C.json', dataType: 'json', success(result) { resolve(result); } }); }); };
△ 用Promise请求数据,但是还没有调用
可以这样调用:
apiA().then(result => { console.log('第一次请求', result); return apiB(); }).then(result => { console.log('第二次请求', result); return apiC(); }).then(result => { console.log('第三次请求', result); console.log('OK'); });
△ 串行调用
△ 图3_调用结果
也可以这样调用
(async function () { let result = await apiA(); console.log('第一次请求', result); result = await apiB(); console.log('第二次请求', result); result = await apiC(); console.log('第三次请求', result); console.log('OK'); })();
△ 调用结果跟上面的是一样的,串行
1 / Promise的语法
△ 图4_直接这样写,会报错
要求new的时候,必须传一个函数进来才可以
△ 图5_new Promise是立即执行的
上图可知,new Promise的时候:
1、 会立即执行传递executor函数
① 在executor函数中可以用来管控一个异步的操作(不写异步的也可以)
② 传递给executor函数两个参数:resolve, reject;这俩参数也是函数
△ 图6_两个参数也是函数
2、创造Promise类的实例p1:
① [[PromiseState]] 是promise的状态:
=> pending准备状态
=> fulfilled/resolve 成功(已兑现)
=> rejected 失败(已拒绝)
② [[PromiseResult]]是promise值:默认是undefined,一般存储成功的结果或者失败的原因
③ p1.__proto__ => Promise.prototype
有公共方法:then/catch/finally
△ 图7_promise实例,PromiseState/PromiseResult 浏览器不一样名字也会不一样,但存储的值是一样的
(1)执行resolve函数
△ 图8_执行resolve函数
执行resolve控制实例的状态变为成功,传递的值100是成功的结果
① [[PromiseState]]:"fulfilled"
② [[PromiseResult]]:100
(2)执行reject函数
△ 图9_执行reject函数
执行reject控制实例的状态变为失败,并传递失败的原因是404【不影响代码执行】
① [[PromiseState]]:"rejected"
② [[PromiseResult]]:404
△ 图10_失败态
如果executor函数中的代码报错,则实例的状态也会变为失败态:
① [[PromiseState]]:"rejected"
② [[PromiseResult]]:报错的原因
(3)resolve和reject俩函数都调用了
△ 图11_谁先执行谁说了算,说一不二
对于promise实例的状态和结果:resolve函数和reject函数,谁先执行谁说了算,另外一个就无效了
即:一旦状态从pending改变为fulfilled或者rejected,都无法再次改变状态了 promise是一个说一不二的主
2 / Promise如何管控异步
let p1 = new Promise(function executor(resolve, reject) { //...COCE }); p1.then(onFulfilled, onRejected);
△ promise实例上的then方法:接收两个函数为参数
(1)没有宏任务时
let p1 = new Promise((resolve, reject)=>{ console.log(1); resolve('ok'); console.log(2); }); p1.then(result =>{ console.log('成功->', result); }, reason =>{ console.log('失败->', result); }); console.log(3);
△ 没有宏任务,执行结果是?
△ 图12_执行过程
代码执行顺序很重要:
① new Promise
② 执行executor:通过执行resolve/reject来改变promise实例的状态和结果
=> 需要【将来通知】then方法的两个参数中的某一个执行
=> 此时,还没有执行到then方法,所以形参函数还没有被注入
=> 先把这个【通知】存起来
③执行p1.then(onFulfilled, onRejected) 将这两个方法注入【需要执行的微任务】
④ 当同步任务执行完以后,去EventLoop里面找需要执行的微任务
=> 拿到【通知】,此时then方法的形参函数已经注入好了
=> 根据p1的状态来执行具体的函数
(2)有宏任务时
let p1 = new Promise((resolve, reject)=>{ // 在new Promise时,立即执行executor函数中管理了一个一个异步编程代码 // 此时状态是pending // 当异步操作达到指定时间,开始执行时【理解为异步操作成功】 // 此时我们执行通过resolve,把promise状态修改为fulfilled,值:'Ok' setTimeout(()=>{ resolve('OK'); }, 1000);});//=> 执行p1.then时,把将来需要执行的函数都注入好了p1.then(result =>{ // 当p1实例的状态修改为fulfilled的时候,通知传递的第一个函数执行 // result -> [[PromiseResult]] console.log('成功->', result);}, reason =>{ // 当p1实例的状态修改为rejected的时候,通知传递的第二个函数执行 // reason -> [[PromiseResult]] console.log('失败->', result);});
Promise 是如何管控异步编程的?
① new Promise 的时候创建一个promise实例,此时在executor函数中管理一套异步的代码
② 后面等异步操作成功或失败的时候,执行resolve/reject,以此控制promise实例的状态和结果
③ 根据状态和结果,就可以控制基于.then
注入的两个方法中的哪一个去执行了
必须要非常、非常、非常地清除代码执行的顺序:
① new Promise
② 执行executor:设置一个异步定时器【宏任务】
③ 执行 p1.then注入两个方法【注入的方法保存起来】【微任务】
-----> 同步代码执行完了,EventLoop里面没有微任务,有宏任务
-----> 等待1000ms后
④ 执行定时器的回调函数:执行resolve改变promise状态和值【通知】
----> 去EventLoop找微任务
⑤ 通知之前基于.then
注入的两个方法中的第一个执行
3 / 小结
1、Promise可以管控异步代码的执行
2、Promise是一个说一不二的主,有决策了,就不反悔
3、Promise变为失败态,有两种:
① 主动调用reject方法
② 报错了,比如:变量没有定义 resolve(a),这个a没有定义,promise实例的[[PromiseState]]:'rejected' [[PromiseResult]]:报错的原因
- end -