异步专题:promise 和 async、await
同步和异步:同步和异步是一种消息通知机制
同步: A调用B,B处理获得结果,才返回给A。A在这个过程中,一直等待B的处理结果,没有拿到结果之前,需要A(调用者)一直等待和确认调用结果是否返回,拿到结果,然后继续往下执行。做一件事,没有拿到结果之前,就一直在这等着,一直等到有结果了,再去做下边的事 异步: A调用B,无需等待B的结果,B通过状态,通知等来通知A或回调函数来处理。 做一件事,不用等待事情的结果,然后就去忙别的了,有了结果,再通过状态来告诉我,或者通过回调函数来处理。如定时器等ES6的Promise对象是一个构造函数,用来生成Promise实例。
所谓Promise对象,就是代表了未来某个将要发生的事件(通常是一个异步操作)。
它的好处在于,有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数
Promise的三种状态:pending 、resolve 和 rejectthen 方法 和 try/catch - 图片加载then的返回值,会返回一个新的 Promise 对象, 但是状态会有几种情况:
- then 的回调函数中没有返回值,then就会返回一个状态为: resolved 的 promise 对象- then 的回调函数返回值是 非 promise 的值, then就会返回一个状态为: resolved 的 promise 对象,另外会把返回值,传递给 下一个 then- then 的回调函数返回值是 promise 对象,then 就直接返回这个 promise 对象,具体的状态可以由我们自己定义,具体传递的值,也由我们自己定义Promise的三种状态:pending 、resolved 和 rejected
创建Promise对象时,即没有执行成功也没有执行失败,则状态为pending;创建Promise对象时,如果对象中是执行成功方法resolve(),则返回状态为resolved;创建Promise对象时,如果对象中是执行失败方法reject(),则返回状态为rejected,且报错;或者通过创建Promise 对象时,是否成功调用接口,从而获得Promise的响应状态 let p = new Promise((resolve,reject)=>{ resolve("执行成功"); // reject("执行失败"); }); //创建Promise对象时,即没有执行成功也没有执行失败,则状态为pending // console.log(p);//Promise {<pending>} //创建Promise对象时,如果对象中是执行成功方法resolve,则返回状态为resolved console.log(p);//Promise {<resolved>: "执行成功"} //创建Promise对象时,如果对象中是执行失败方法reject,则返回状态为rejected,且报错 // console.log(p);//Promise {<rejected>: "执行失败"}示例:图片下载
如果图片下载成功走第一个方法,如果图片下载失败走第二个方法
let p = new Promise((resolve,reject)=>{ let img = new Image(); img.src = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1565026842061&di=796e2653d34dfe2c98a7b8538fae2aca&imgtype=0&src=http%3A%2F%2Fimages.cnblogs.com%2Fcnblogs_com%2Fidotnet8%2Fchina.gif"; img.onload = function(){ resolve("图片加载成功"); }; img.onerror = function(){ reject("图片加载失败"); }; }); // p.then(success=>{ console.log(success);//图片加载成功 },error=>{ console.log(error);//图片加载失败 });then的返回值,会返回一个新的 Promise 对象, 但是状态会有几种情况:
then 的回调函数中没有返回值,then就会返回一个状态为: resolved 的 promise 对象。如果第一个then方法没有返回值,则无论这个then走的是成功还是失败方法,都不会影响下一个then方法的执行。 then 的回调函数返回值是 非 promise 的值, then就会返回一个状态为: resolved 的 promise 对象,另外会把返回值,传递给 下一个 thenthen 的回调函数返回值是 自定义的promise 对象,then 就直接返回这个 promise 对象,具体的状态可以由我们自己定义,具体传递的值,也由我们自己定义(此时如果自定义的Promise对象返回resolve,会将成功信息传递给下一个then的第一个方法;如果自定义的Promise对象返回reject,会将失败信息传递给下一个then的第二个方法)。 then没有返回值时(then没有返回值时,默认所有then方法同时执行):then就会返回一个状态为: resolved 的 promise 对象 let p = new Promise((resolve,reject)=>{ resolve("成功"); }); //then返回值一:如果then 方法没有返回值,就会返回一个状态为resolved的promise对象(即没有返回值默认走第一个成功的函数) p.then(success=>{ console.log(success);//成功 第一个then方法没有返回值,所以默认走这个成功的方法 },error=>{ console.log(error); }).then(info=>{ console.log(info);//undefined 由于第一个then没有返回值,而第二个then是根据第一个then的返回值接收信息的,所以info为undefined }); then有返回值,但是返回值是非promise的值时:then就会返回一个状态为: resolved 的 promise 对象,另外会把返回值,传递给 下一个 then let p = new Promise((resolve,reject)=>{ resolve("成功"); }); //then返回值二:当then方法有返回值,但返回值时非promise对象时,直接将返回值传递给下一个then p.then(success=>{ console.log(success);//成功 return success; },error=>{ console.log(error); }).then(info=>{ console.log(info);//成功 当then方法有返回值,但返回值时非promise对象时,直接将返回值传递给下一个then,所以这里的then方法得到的信息时第一个then的返回值 }); then 的回调函数返回值是 promise 对象,then 就直接返回这个 promise 对象,具体的状态可以由我们自己定义,具体传递的值,也由我们自己定义 此时如果自定义的Promise对象返回resolve,会将成功信息传递给下一个then的第一个方法如果自定义的Promise对象返回reject,会将失败信息传递给下一个then的第二个方法 let p = new Promise((resolve,reject)=>{ resolve("成功"); }); //then返回值三:当then方法有返回值,且返回值为Promise对象时,then方法直接返回这个Promise对象,具体的状态和值由我们自己决定 // 此时如果自定义的Promise对象返回resolve,会将成功信息传递给下一个then的第一个方法 // 如果自定义的Promise对象返回reject,会将失败信息传递给下一个then的第二个方法 p.then(success=>{ return new Promise((resolve,reject)=>{ // resolve("第一个then方法成功"); reject("第一个then方法失败"); }); },error=>{ console.log(error); }).then(succMsg=>{ console.log(succMsg); },errMsg=>{ console.log(errMsg); });需求:三个数据请求,请求1依赖请求2,请求2请求3。即请求1必须在请求2执行完成后才能执行,请求2必须在请求3执行完了才能执行
//Promise异步即以同步的方式实现异步 async function fn(){ await new Promise((resolve,reject)=>{ setTimeout(function(){ console.log("请求3"); resolve(); },1000); }); await new Promise((resolve,reject)=>{ setTimeout(function(){ console.log("请求2"); resolve(); },1000); }); await new Promise((resolve,reject)=>{ setTimeout(function(){ console.log("请求1"); resolve(); },1000); }); } fn();async函数和await方法中,其中一个await执行失败,就不再执行之后的程序:
如下第二个await执行reject方法代表执行失败,执行失败后,请求1不会再继续执行
async function fn(){ await new Promise((resolve,reject)=>{ setTimeout(function(){ console.log("请求3"); resolve(); },1000); }); await new Promise((resolve,reject)=>{ setTimeout(function(){ console.log("请求2"); reject(); },1000); }); await new Promise((resolve,reject)=>{ setTimeout(function(){ console.log("请求1"); resolve(); },1000); });Promise.all([p1,p2,p3])的返回值,再调用then方法无论有没有执行失败的方法,都是走第一个参数(成功)
let p1 = new Promise((resolve,reject)=>{ setTimeout(function(){ resolve("p1执行成功"); },1000); }); let p2 = new Promise((resolve,reject)=>{ setTimeout(function(){ // resolve("p2执行成功"); resolve("p2执行失败"); },1000); }); let p3 = new Promise((resolve,reject)=>{ setTimeout(function(){ resolve("p3执行成功"); },1000); }); //Promise.all([p1,p2,p3])只有所有的promise对象都是返回resolved状态,才会成功 let allP = Promise.all([p1,p2,p3]); console.log(allP); //Promise.all([p1,p2,p3])的返回值,再调用then方法无论有没有执行失败的方法,都是走第一个参数(成功) allP.then(msg=>{ console.log("成功"); // console.log(msg);//(3) ["p1执行成功", "p2执行成功", "p3执行成功"] console.log(msg);//(3) ["p1执行成功", "p2执行失败", "p3执行成功"] },info=>{ console.log("失败"); });Promise.race([p1,p2,p3])方法的返回值,再调用then方法时,无论其中的方法执行成功还是失败,都是走then方法的第一个参数(成功)
//Promise.race([p1,p2,...])方法中那个方法执行的最快,就返回哪个方法的返回值 let p1 = new Promise((resolve,reject)=>{ setTimeout(function(){ resolve("p1执行成功"); },1000); }); let p2 = new Promise((resolve,reject)=>{ setTimeout(function(){ // resolve("p2执行成功"); resolve("p2执行失败"); },500); }); let p3 = new Promise((resolve,reject)=>{ setTimeout(function(){ resolve("p3执行成功"); },3000); }); let raceP = Promise.race([p1,p2,p3]); console.log(raceP); //Promise.race([p1,p2,p3])方法的返回值,再调用then方法时,无论其中的方法执行成功还是失败,都是走then方法的第一个参数(成功) raceP.then(msg=>{ console.log("成功");//成功 console.log(msg);//p2执行失败 },info=>{ console.log("失败"); console.log(info); });async await的底层是通过Generator逐渐演变过来的。现在有了async await方法,基本上Generator已经很少使用。但是一些底层的框架可能会用到。
迭代器实现原理 [Symbol.iterator] :
obj[Symbol.iterator] = function(){ return { next(){ return { value: this.i++, done: false } } } }for ...in 迭代的是数组的key值:
// for ...in 迭代的是数组的key值 var arr = [1,2,3,4,5]; for(var index in arr){ console.log(index);//0,1,2,3,4 }for...of 实现迭代,迭代的是数组的value值:
// for ...of 迭代的是数组的value值 var arr = [1,2,3,4,5]; for(var val of arr){ console.log(val);//1,2,3,4,5 }对象可以使用for...in 循环,但是不能使用for...of循环,因为对象不是可迭代对象,没有实现Symbol.iterator方法:
//对象使用for ...in循环 let obj = { a:1, b:2 }; for(let o in obj){ console.log(o);//a b }对象使用for...of循环直接报错:
//对象使用for ...of循环 let obj = { a:1, b:2 }; for(let val of obj){ console.log(val);//for...of迭代.html:22 Uncaught TypeError: obj is not iterable }
结果:
在形式上,Generator是一个普通函数,但是有两个特征。
一是,function命令与函数名之间有一个星号。二是,函数体内部使用yield语句,定义遍历器的每个成员,即不同的内部状态调用Generator函数不会立即执行,而需要调用Generator函数的next()方法后才会执行
### Generator 语法
// Generator 语法 function* gen() { yield 1; yield 2; yield 3; } //调用Generator函数不会立即执行 let g = gen(); console.log(g.next());//{value: 1, done: false}自执行Generator函数:
co函数:自定义自动化generator函数调用器。判断上一个异步执行完成后再执行下一个异步函数
function*fn(){ yield new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("a"); resolve(1); },500); }); yield new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("b"); resolve(2); },500); }); yield new Promise((resolve,reject)=>{ setTimeout(()=>{ console.log("c"); resolve(3); },500); }); } co(fn); function co(fn){ let f = fn(); next(); function next(msg){ let result = f.next(); if(!result.done){//done为true时表示走完了 result.value.then(info=>{ console.log(info,msg);//data表示上一步的返回信息 //上一个异步走完了,再执行下一个异步 next(info); }); } } }结果:
通过三种方式实现:回调地狱(不断重复调用);Promise的then方法;Promise的async/await方法
需求:自定义封装动画简易动画框架,让box 从左到右,再从上到下,再从右向左,再从下往上移动200px。