js里的同步与异步
浏览器是多线程的,js是单线程的(浏览器只分配一个线程来执行js)。一个进程包含多个线程,例如在浏览器打开一个应用就占用了一个线程。
同步:在一个线程(主栈)上,同一个时间只能做一件事件,当前事情完成了才能继续进行执行下一个事情。
异步:在线程(主栈)上执行任务,当发现某个任务是一个异步操作时,我们会把这个异步操作放到等待队列中,如果主栈执行完成,监听者会把等待队列里到达时间的异步任务放回主栈执行。
异步操作有:
宏任务:定时器(setinterval、settimeout)、事件绑定、ajax、回调函数、Node中fs可以进行异步的I/O操作、setImmediate(它也是定时器,是Node中独有的异步API)。
微任务: process.nextTick、Promise(async/await)
执行顺序:同步-微任务-宏任务
来看个例子:
上例中,主栈任务有console.log(4),等待队列里有5ms后执行console.log(2) ,20ms后执行console.log(1),500ms后执行console.log(3) 所以最终结果是4 2 1 3 ,如果本例里while里i <=10那么执行这个只需要1-2ms,此时结果就是 4 2 3 1 总结就是先执行主栈再执行等待队列,等待队列里谁先到达时间就先把谁拿到主栈里执行。
ajax里的异步看下图:
Promise的作用
ES6中新增的Promise对象,它是操作异步使用的方法,会解决回调地狱的问题。我们知道,js在进程是单线程的,也就是代码在同步进行解析。有些时候,异步操作是有必要的,在没有promise之前,我们一般使用回调函数,加一个延时执行来实现这一功能,例如:
这种回调函数执行异步的方法,如果嵌套了多个异步执行方法就会产生回调地狱,如下图:
在ES6以后,出现了Promise对象,这个在实现异步操作,会解决回调地狱的问题。
Promise基本语法
new Promise( function(resolve, reject) {
…
}
/* executor */
)
promise 有三种状态,并且一旦发出请求后,
就不能终止,并且状态不可逆,状态也不能再次发生改变!
pending (等待)
fulfilled (完成)
rejected (未完成)
那么我们如何让状态发生改变呢?在Promise对象中,提供了两个方法resolve( ) 和 reject( )
来看个例子:
Promise.resolve和Promise.reject
Promise.resolve( )将现有的东西转成一个Promise对象,resolve状态,成功状态。
let p1 = new Promise( resolve =>{
resolve(‘aaa’);
});
console.log(p1);
等价于Promise.resolve(‘aaa’)
Promise.reject( )将现有的东西转成一个Promise对象,reject状态,失败状态。
Promise.all和Promise.race
Promise还可以并行执行异步任务,有两个方法 all() 和 race()
先来看个例子:
let p1 = Promise.resolve(‘aaa’);
let p2 = Promise.resolve(‘bbb’);
let p3 = Promise.resolve(‘ccc’);
Promise.all( [p1,p2,p3] ).then(res =>{
console.log(res); // [‘aaa’,‘bbb’, ‘ccc’]
});
Promise.race( [p1,p2,p3] ).then(res =>{
console.log(res); // aaa
});
再来看个Promise.all的例子:
试想王者荣耀这个游戏,多名玩家的游戏全部加载到100%,才开始进入游戏开局页面。
race( ) 方法同理,只是哪个先执行完,就返回哪一个。
总结
Promise 是用来管理异步编程的,它本身不是异步的,new Promise的时候会立即把executor函数执行,只不过我们一般会在executor函数中处理一个异步操作。比如下面代码中,一开始是会先打印出2。
let p1 = new Promise(()=>{
setTimeout(()=>{
console.log(1)
},1000)
console.log(2)
})
console.log(3) // 2 3 1
Promise 采用了回调函数延迟绑定技术,在执行 resolve 函数的时候,回调函数还没有绑定,那么只能推迟回调函数的执行。这具体是啥意思呢?我们先来看下面的例子:
new Promise的时候先执行executor函数,打印出 1、2,Promise在执行resolve时,触发微任务,还是继续往下执行同步任务, 执行p1.then时,存储起来两个函数(此时这两个函数还没有执行),然后打印出3,此时同步任务执行完成,最后执行刚刚那个微任务,从而执行.then中成功的方法。