在开始谈javascript异步编程之前,我们先抛出几个问题,针对问题回答问题,从而解决问题
1、为什么要有异步编程?
2、异步编程怎么使用?
3、异步编程的原理是什么?
对javascript有点了解的人都知道,它是单线程的,这是js语言特性所决定的,它的作用就是用来操作DOM树的,而DOM树经过浏览器渲染后就是我们看到的可视化UI界面,同一时间只允许一个js操作命令修改DOM,若有多个命令时,浏览器不知道要渲染那个,会出现问题。比如你的主线程在给DOM的innerHtml做一个赋值操作,你的另一个线程把这个DOM结构删除了,这肯定不可以。但是由于页面的结构和资源越来越复杂多样,比如需要网络请求的图片资源、ajax、轮询、文件资源等,在单线程的情况下,必须等一个任务执行完毕之后才能进行下一项,这样非常耗时,这种情况对于下面的程序来说是一种阻塞;而且用户体验不好,这个时候如果我们在等待的时间里执行其他操作,在这个操作完成之后再告诉我们,这就是异步编程。一般我们遇到的异步操作包括callback、Promise、setTimeout 、setInterval、事件
在讲解之前假设已经了解Event Loop,同步代码放到执行栈中,异步代码放在队列当中,程序一般的执行顺序是执行栈中代码执行完毕后执行队列中的命令,而异步代码任务又分为微任务(Promise)和宏任务,所以执行代码的顺序就变为执行栈>微任务>宏任务。异步与同步相比,最难以掌控的就是异步的任务会什么时候完成和完成之后的回调问题
本节主要讲解Promise,前面提到在异步编程中,一个异步任务执行的过程中,去执行另一个没有影响的异步代码,没有影响是指这个异步任务的执行过程不依赖于前一个异步任务的结果,在前面那个异步任务执行完成后或成功或失败都发出通知,这个就是promise:promise又三种状态,pending(进行中)、resolve(已完成)、rejected(已拒绝),开始执行的时候,promise状态就是pending,任务完成了状态就变成了Resolved、没有完成失败了就变成了Rejected。
let promise = new Promise((resolve,reject)=>{
// 接收一个callback。参数是成功函数与失败函数
setTimeout(()=>{
let num = parseInt(Math.random()*100);
// 如果数字大于50就调用成功的函数,并且将状态变成Resolved
if(num > 50){
resolve(num);
}else{
// 否则就调用失败的函数,将状态变成Rejected
reject(num)
}
},10000)
})
promise.then(res=>{
console.log(res); //在构造函数中如果你执行力resolve函数就会到这一步
},
err=>{ // 执行了reject函数会到这一步
console.log(err);
}
)
then方法接收两个函数,第一个是承诺成功(状态为resolved)的回调函数,一个承诺失败(状态为rejected)的回调函数。
then方法的返回值不是一个promise对象就会被包装成一个promise对象,所以then方法支持链式调用。
还有一个promise.all和promise.race
promise.all接受一个数组,数组里都是promise对象,当所有promise对象状态变为resolved时才会变为resolved,但是若有一个时rejected,则promise.all状态就会变为rejected;该情况可以解决程序并行任务的问题,调用then方法时,回调函数是一个按顺序执行成功后结果的数组
promise.race同样也是接受一个promise对象的数组,它是一个竞速模式,不同的是当所有promise对象中,只要有一个promise对象的状态变为rejected或resolved,它的状态就会随之改变,第一个状态改变的promise对象的返回值被使用。