Promise的总结
(1)什么是同步,异步?
同步表示需要前一个任务完成之后,才会执行下一个任务,简而言之,就是上一行代码执行返回结果后,才会执行下一行代码(好理解)
异步表示异步任务启动后,会继续向下执行,等到同步代码执行结束之后才执行回调函数;
//异步执行
function func1() {
console.log('start');
setTimeout(()=>{
console.log('i am callback');
},3000);
console.log('end');
}
//这个函数会先执行console.log('start')-,接着执行setTimeout(),但是setTimeout()里面的回调函数是一个异步函数,所以先放起来,继续执行后面的console.log('end');然后发现所有的同步代码执行完毕后,再去执行setTimeout()里面的回调函数,这里需要注意的是不是不执行setTimeout,而是里面的异步的回调函数不执行
// 因此 打印的顺序为 start end 然后等3秒之后打印出 i am callback
(2)回调函数
回调函数是ES5里面解决异步顺序的一种方法,但是这种方法的可读性很差,当出现回调函数层层嵌套的情况时,可维护性差,可读性差,也就是出现了所谓的回调地狱。
function fn(cb) {
setTimeout(() => {
cb();
}, 1000);
}
fn(function () { // 回调地狱 ;
console.log(111);
fn(function () {
console.log(222);
fn(function () {
console.log(333);
fn(function () {
console.log(444);
})
})
})
})
如何解决回调地狱的问题呢:
(1)Es6中的Promise
(2)async await
(3)观察者模式
(3)Promise
resolve和reject方法
1.pending 没有任何操作
2.resolved/fulfilled 成功的状态
3.rejected 失败的状态
function fn(){
let p = new Promise((resolve,reject)=>{
})
console.log(p);
}
// resolve()函数的作用就是将PromiseState从pending状态更新为fulfilled的状态,resolve(param)的参数会作为[[PromiseResult]]的值
function fn1(){
let p = new Promise((resolve,reject)=>{
resolve()
})
console.log(p);
}
function fn2(){
let p = new Promise((resolve,reject)=>{
resolve(‘123)
})
console.log(p);
}
// reject()函数的作用就是将PromiseState从pending状态更新为 rejected的状态, rejected(param)的参数会作为[[PromiseResult]]的值
function fn1(){
let p = new Promise((resolve,reject)=>{
reject(param)
})
console.log(p);
}
then方法
每一个promise对象都会有一个then方法
then方法里有2个回调函数,onResolved和onRejected。
1.onResolved: 在调取 resolve函数的时候执行 (then里的第一个回调函数)
promiseState : fufilled 状态的时候
promiseResult: 作为 onResolved函数的参数;
2.onRejected : 在调取reject 函数的时候执行;(then里的第二个回调 函数);
promiseState : rejected 状态的时候
promiseResult: 作为 onRejected函数的参数;
1.onResolved回调函数 参数 是promiseResult的结果值;
代码如下:
/*
调用 resolve 的时候 promiseState 变成 fufilled状态
promiseResult 是传递的参数 data
*/
function fn(){
let p = new Promise((resolve, reject) => {
const data = '1234'
resolve(data);
})
console.log(p);
p.then(function (res) { // 是成功状态的promise对象的promiseResult结果值;
console.log("onResolved执行了", res);
}, function () {
console.log("onRejected执行了");
});
}
// resolve函数在异步回调里面,所以先执行同步语句,然后在执行异步语句,所以执行同步语句console.log(p)的时候,还没有执行resolve,p的状态还是pending,然后在多
function fn(){
let p = new Promise((resolve, reject) => {
setTimeout(() => {
const data = '1234'
resolve(data);
// reject("错误");
}, 2000);
})
console.log(p);
p.then(function (res) { // 是成功状态的promise对象的promiseResult结果值;
console.log("onResolved执行了", res);
}, function () {
console.log("onRejected执行了");
});
}
catch方法
如果catch之前的任意一个阶段发生了错误,那么就会触发catch,之后的then就不会被执行,这个和同步编程里面用到的try…catch…块很相似。
Promise用法技巧
(1)
上面这种写法会打破两个promise的并行,他会等到第一个任务执行完毕之后才执行第二个任务,更高效的写法是用promise.all
组合起来,然后再去 await,修改后的程序运行效率会提升一倍。
(2)如果我们希望等待循环中所有的异步操作都执行完毕后再继续执行后面的代码,是不能直接使用forEach
或者map
这一类方法的,如下图,尽管我们在回调函数中写了await,但是这里的forEach
会立刻返回,他并不会暂定等到所有的异步方法都执行完毕。
如果我们希望等待循环中所有的异步操作都执行完毕后再继续执行后面的代码,正确的写法还是应该使用传统的for循环。
更进一步,如果我们想要循环中的所有操作都并发执行的话,一种更炫酷的写法就是使用for await
,
重点,宏任务,微任务,同步,异步,Event Loop模型以及执行顺序
js是单线程(自上而下执行代码),因此一个任务时间过长,势必会造成线程堵塞,影响渲染效率;
随着Promise的引入,JavaScript引擎自身也能够发起异步任务了,于是,js任务就分为了同步任务和异步任务,异步任务由进一步划分为
宏任务
和微任务
,所以说宏任务
和微任务
都是异步任务的范畴。微任务执行比较快,一次可以执行很多个,在当前宏任务执行后立刻清空微任务可以达到伪同步的效果,这对视图渲染效果起到至关重要的作用,这也是区分宏任务、微任务的原因。
宏任务的微任务的基本类型
new Promise()内部的代码是同步任务
宏任务:setTimeout(), setInterVal() ,异步Ajax请求
微任务:Promise.then(), Promise.catch()和Promise.finally(),process.nextTick()
宏任务和微任务执行过程
先执行同步代码,遇到异步宏任务就放到宏任务队列中,遇到异步微任务就放到异步微任务队列中,当所有的同步代码执行完毕后,将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环到所有的任务执行完毕。
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
1)执行第一行的同步任务,打印1
2)第一个setTimeout加入任务队列宏任务区,记为s1
3)第15行的process.nextTick是异步任务中的微任务,加入微任务区,记为n1
4)第18行的promise是同步任务,打印7,其后的.then是微任务,加入微任务区,记为t1
5)第24行的setTimeout加入任务队列宏任务区,记为s2。至此,同步代码执行完毕。
6)执行微任务区中的任务,n1打印6,t1打印8。至此,微任务区任务执行完成
7)执行宏任务区s1,打印2,其内process.nextTick放入微任务区,记为n2,其后promise为同步任务,立即执行打印4,将后续.then微任务放入微任务区记为t2
8)继续执行微任务区,n2打印3,t2打印5
9)执行第二个setTimeout,直接打印9,其内process.nextTick放入微任务区,记为n3,其后promise为同步任务,立即执行打印11,将后续.then微任务放入微任务区记为t3
10)执行微任务,n3打印10,t3打印12
最终打印结果为:1、7、6、8、2、4、3、5、9、11、10、12