一、异步函数与同步函数的操作顺序
例子1:异步爸爸和异步儿子、同步儿子的故事
async function myAsync(){ timer= await setInterval(()=>{ console.log(count) count++; if(count>10){clearInterval(timer)} console.log("我是异步") },100) console.log("我是同步") }
myAsync()
if和console.log()是属于同一级别的,但是因为if里面有异步函数,所以比同步慢执行;
例子2:异步爸爸和异步儿子,与同步兄弟的故事
async function myAsync(){ timer= await setInterval(()=>{ console.log(count) count++; if(count>10){clearInterval(timer)} console.log("我是异步") },100) console.log("我是同步1") } myAsync() console.log("我是同步2")
console.log("同步2")与myAsync是同一级,但是myAsync是异步函数,所以先输出同步2,又因为例子1说明的原因,所以再输出同步1,最后执行我是异步。
例子3:同步爸爸与异步儿子,与同步兄弟的故事
function myAsync(){ timer= setInterval(()=>{ console.log(count) count++; if(count>10){clearInterval(timer)} console.log("我是异步") },100) console.log("我是同步1") } myAsync() console.log("我是同步2")
此时由于myAsync与console.log()都是同一级别的,所以遵循从上到下执行,又因为myAsync的儿子是异步,所以myAsync的儿子最慢执行。
例子4:
async function myAsync1(){ timer= setInterval(()=>{ console.log(count) count++; if(count>10){clearInterval(timer)} console.log("我是异步1") },100) } async function myAsync2(){ setTimeout(()=>{ console.log("我是异步2") },100) } myAsync1() myAsync2() console.log("我是同步2")
同样,同步最先执行,两个异步都是同级,按时间触发顺序,执行
把例子4的两个异步调一下个:
async function myAsync1(){ timer= setInterval(()=>{ console.log(count) count++; if(count>10){clearInterval(timer)} console.log("我是异步1") },100) } async function myAsync2(){ setTimeout(()=>{ console.log("我是异步2") },100) } myAsync2() myAsync1() console.log("我是同步2")
根据上面几个例子说明:
(1)同一级别下,同步异步执行顺序都是同步函数最先执行,异步函数最慢执行
(2)而两个一样的异步函数,按照顺序,上面的先执行,下面的慢执行
(3)两个不一样的异步函数,要比较异步函数内部是否有继续嵌套异步,还是嵌套同步,有同步的先执行;
(4)两个一样的异步执行函数,但是时间触发不一样的,按照时间触发先后,按顺序执行,这也就是实际开发中,你并不知道你向服务器索取的资源哪一个先拿到。先拿到资源的,就先执行,但是这个时间是未知的。
------------------------------------------------------------------------------------------------------------------------------------------开始讲些有用的:
写个例子:
let arr1 = [] function myAsync(){ setTimeout(()=>{ return arr1.push(1);//对数组赋值 },1000) } let num = myAsync(); console.log(num);
运行结果:
答案很明显,赋值时由于异步函数setIterval还没操作,就赋值了,明显赋了个寂寞,所以undefined;
那么早期的时候,我们是怎么拿到异步函数里面的值的呢?
二、早期与异步函数的爱恨情仇:
利用回调函数!看下面这个例子:
let arr1 = [] function myAsync(callback){ setTimeout(()=>{ return callback(arr1.push(1));//对数组赋值 },1000) }
传入一个参数callback,这个参数其实就是一个函数,那么怎么取值?
// 箭头函数写法 myAsync((arg)=>{ console.log(arg);//arg为参数,其实就是拿到了arr1.push里面的内容 }) //es5写法 myAsync(function callback(arg){ console.log(arg) })
执行顺序分析:首先执行myAsync(),执行完毕后回调callback(),开始执行,这就是所说的回调函数,说白了就是先执行myAsync(),接着执行callback,如何callback内部把内容输出。所以拿到值的其实是callback帮忙拿到的。
有了上面这个思路,所以我们的前辈们很开心,找到了探索新世界的大门,用之而来请求网络数据时非常开心,因为可以拿到异步操作里面的值了!
然而开心了没多久,开始难过了,此时出现了一个怪物:
他叫回调地狱,为什么会有它呢,其实实际应用场景需要先完成第一个异步任务,才能执行第二个异步任务,接着才能完成第三个异步任务,具体例子我现在还没想到,知道的可以评论一下,因为我是个菜鸡。
接着你看一下下面这个例子:
async function myAsync1(callback){ setTimeout(()=>{ callback(arr1.push("任务1,我需要最先完成")) },1000) } async function myAsync2(callback){ setTimeout(()=>{ callback(arr1.push("任务2,我需要第二完成")) },1000) } async function myAsync3(callback){ setTimeout(()=>{ callback(arr1.push("任务3,我需要最慢完成")) },1000) } function totalTasks(){ myAsync1(cb1=>{ // 若cb1非空,就是cb1初始化了,可以操作cb1了,专业点说法就是它加载入内存了,,才能继续下面执行cb2,,下面依次类推 if(cb1){ console.log(arr1) myAsync2(cb2=>{ if(cb2){ console.log(arr1) myAsync3(cb3=>{ if(cb3){ console.log(arr1); } }) } }) } }) } totalTasks()
结果:
这还是仅仅三层回调,那么要是多层回调,再写成网络请求格式的,你会写到一半自己线绕晕了。
所以前辈们写这个的时候表情是这样子的:
想拉屎,但是又拉不出来的感觉。
三、promise救世主的出现
愚笨而又可爱的人类啊,我来拯救你们啦:
此时我们只要这样写就好了:
async function myAsync(){ return await new Promise(res=>{ setTimeout(()=>{ arr1.push(1) res(arr1) },100) }) } let p = myAsync() p.then((data)=>{ console.log(data) })
promise呢,其实也是用的回调的思想,但是他这种语法格式我们用起来理解就简单多了,维护起来也方便。
它执行的原理就是回调,它有一个参数,这个参数是一个回调函数;而这个回调函数又有两个参数,第一个参数是resolve,表示成功取到值的操作,第二个,表示请求被拒绝的操作,这两个参数也都是回调函数,如果操作成功,resolve则会执行,并return本身携带的参数;reject亦然。
下面看个例子,讲一下里面的各个参数的作用:
new Promise( function (resolve, reject) { // 一段耗时的异步操作
settimeout(()=>{
if(n>10)
{ resolve('成功') // 数据处理完成
}else{
reject('失败') // 数据处理出错
}
},10000)
}).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失败 )
resolve(参数),如果数据请求成功,它会把参数发给then,以备进行下一步操作,reject(参数),如果数据请求错误,他会把参数传给then以备下一步操作。
而then函数它其实也是一个回调函数,它的两个参数是用来接收上一个函数传过来的值,then函数可以传给下一个then函数,子子孙孙无穷尽也。
形如:
new Promise( function (resolve, reject) { // 一段耗时的异步操作 resolve('成功') // 数据处理完成 reject('失败') // 数据处理出错 } ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失败 ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失败 ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失败 ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失败 )
其实还有一个catch函数,它的作用相当于then函数的第二个参数,也相当于promise的第二个参数,都是用来处理错误操作的:
new Promise( function (resolve, reject) { // 一段耗时的异步操作 resolve('成功') // 数据处理完成 // reject('失败') // 数据处理出错 } ).then( (res) => {console.log(res)}, // 成功 (err) => {console.log(err)} // 失败 ).catch{ console.log(err) }
具体为什么多次一举,加个catch,因为前面的then函数已经可以处理错误操作了,网上说还是可能异步引起的错误,有些错误只有catch才能发现,这个等我变成大神后再说下原因。
四、promise,与async 与await实现异步操作按顺序执行:
// 2.await async 与promise async function myAsync1(){ return await new Promise(res=>{ setTimeout(()=>{ console.log("任务1") res("任务1") },1000) }) } async function myAsync2(){ return await new Promise(res=>{ setTimeout(()=>{ console.log("任务2") res("任务2") },10) }) } async function myAsync3(){ return await new Promise(res=>{ setTimeout(()=>{ console.log("任务3") res("任务3") },1) }) } async function totalTask(){ await myAsync1(); await myAsync2(); await myAsync3(); } totalTask()
直接用async 与await实现异步函数顺序执行。
注:async与await关系密切,只有声明了async的函数,里面才能使用await。await作用就是等待这个函数执行,等待执行完再执行下一个。
好了,写到这里了,我好帅啊!