PromiseA+ 浏览器的JS异步 宏任务/微任务 概念
js是单线程语言,执行顺序是单线程异步模型,所以有宏任务/微任务机制来处理异步操作 三种机制任务:同步任务、(异步)宏任务、(异步)微任务 宏任务:以下任务的回调函数
I/O setTimeout setInterval requestAnimationFrame setImmediate 微任务
process.nextTick Promise.then / .catch / .finally async/await MutationObserver
注意区分,setTimeout自身并不是宏任务,宿主环境将setTimeout的回调任务推入延迟队列。 延迟队列中计时到了,才会将回调函数推入宏任务队列 其他同理 整个script脚本作为同步任务推入执行栈
遇到宏任务推入宏任务队列 遇到微任务推入微任务队列 本轮事件执行完毕后,将微任务队列全部按序推入执行栈 微任务队列队列清空后,宏任务的第一个任务出列 再清空微任务队列,再出列一个宏任务...
setTimeout一秒后进入宏任务队列 同步任务执行了两秒(多) 结果宏任务还是等到同步任务执行完毕才执行
setTimeout ( ( ) => console. log ( 'setTimeout' ) , 1000 ) ;
const current = Date. now ( ) ;
while ( Date. now ( ) < current + 2000 ) { }
console. log ( '同步' ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13
无论定义顺序怎样,总是同步任务 -> 微任务 -> 宏任务
同步代码执行完,宏任务和微任务2直接进队列 微任务2、宏任务执行完 1秒后微任务1才被推进微任务队列 setTimeout ( ( ) => console. log ( '宏任务' ) , 0 ) ;
const p1 = new Promise ( resolve => setTimeout ( resolve, 1000 ) ) . then ( res => console. log ( '微任务1' ) ) ;
const p2 = Promise. resolve ( ) . then ( res => console. log ( '微任务2' ) ) ;
console. log ( '同步任务' ) ;
1 2 3 4 5 6 7 8 9 10 11
浏览器多线程 JS引擎线程 GUI渲染线程(渲染页面) 事件触发线程 定时器触发线程 http请求线程 其他线程 递归函数的风险 递归函数是函数调用自身。 递归函数是有风险的。因为递归函数会触发大量的栈帧堆积,让执行栈高度很高,可能会触发栈溢出。 优化
尾调用优化 将递归写成同步函数(while) 递归函数放到异步函数里面(会一直变换任务队列,会比同步代码慢) 实现阶乘函数 递归函数如果没被释放会有栈帧
,根据环境栈会有上限值,超过就会出现爆栈 function factorial ( i ) {
console. log ( i) ;
return i === 1 ? i : i * factorial ( i - 1 ) ;
}
console. log ( factorial ( 100000 ) ) ;
1 2 3 4 5 6 7 8 9 10
尾调用优化:函数return递归函数,本函数不依赖递归函数的返回值做计算,本函数就会被释放。 目前没有环境支持JS尾调用优化写法,但是可以用异步配合将其实现,防止爆栈
递归返回一个计时器,以参数的形式传递了计算值,没有与本函数变量计算的地方,所以本轮调用栈清空,进入下一轮事件轮询 返回Promise,让外部能拿到最终计算的值
function factorial ( i ) {
return new Promise ( resolve => {
const deepFn = ( num, total ) => {
console. log ( num) ;
if ( num === 1 ) {
return resolve ( total) ;
}
total = total * num;
return setTimeout ( ( ) => deepFn ( num - 1 , total) ) ;
}
return i <= 1 ? Promise. resolve ( i) : deepFn ( i, 1 ) ;
} )
}
factorial ( 100000 ) . then ( res => console. log ( res) ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
setTimeout是将事件存储到宿主环境的延迟列表里面,存取效率会比较低 用while将递归函数换成同步执行的代码也能实现同样的目标,效率会高很多(mac2014十倍左右)
function factorial ( i ) {
if ( i <= 1 ) {
return i;
}
let total = i;
while ( -- i !== 1 ) {
console. log ( i) ;
total = total * i;
}
return total;
}
console. log ( factorial ( 100000 ) ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
function factorial ( i ) {
let cPromise = Promise. resolve ( i) ;
while ( -- i > 1 ) {
console. log ( i) ;
const scopedI = i;
cPromise = cPromise. then ( res => res * scopedI) ;
}
return cPromise;
}
factorial ( 1000000 ) . then ( res => console. log ( res) ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
尾调用优化对比 同样是异步任务,Promise微任务队列处理速度更快 console. time ( "run" ) ;
const factorial = async ( num ) => {
let total = 1 ;
let c = num;
return new Promise ( ( res ) => {
const deepHandler = ( ) => {
if ( c === 1 || c === 0 ) {
return res ( c * total) ;
}
total = c * total;
c = c - 1 ;
Promise. resolve ( ) . then ( deepHandler) ;
} ;
deepHandler ( ) ;
} ) ;
} ;
const s = 50000 ;
factorial ( s) . then ( ( res ) => {
console. log ( res) ;
console. timeEnd ( "run" ) ;
} ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
无限深度嵌套Promise.then
也不快,因为这个一直在改变对象 console. time ( "run" ) ;
const factorial = async ( num ) => {
let cPromise = Promise. resolve ( num) ;
while ( num > 1 ) {
num-- ;
cPromise = cPromise. then ( res => res * num) ;
}
return cPromise;
} ;
const s = 50000 ;
factorial ( s) . then ( ( res ) => {
console. log ( res) ;
console. timeEnd ( "run" ) ;
} ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
宏任务 调用:setTimeout(fn, time[, args...]) function say ( name, age ) {
console. log ( ` 我叫 ${ name} ,我今年 ${ age} 岁啦! ` ) ;
}
setTimeout ( say, 2000 , 'hdy' , 18 ) ;
1 2 3 4
const timer = setTimeout ( ( ) => console. log ( '不会执行' ) , 2000 ) ;
clearTimeout ( timer) ;
1 2
调用:setInterval(fn, time[, args...]) function say ( name, age ) {
console. log ( ` 我叫 ${ name} ,我今年 ${ age} 岁啦! ` ) ;
}
setInterval ( say, 2000 , 'hdy' , 18 ) ;
1 2 3 4
const timer = setInterval ( ( ) => console. log ( '不会执行' ) , 2000 ) ;
clearInterval ( timer) ;
1 2
setTimeout保证等待time后执行 setInterval在每次执行完后再计时 如果要保证每次的间隔的话可以选择setTimeout递归
const run = ( ) => {
const now = Date. now ( ) ;
const end = now + 1000 ;
console. log ( now) ;
while ( Date. now ( ) < end) { }
console. log ( '本人耗时1秒,要求间隔两秒执行' ) ;
setTimeout ( run, 2000 ) ;
}
setTimeout ( run, 2000 ) ;
1 2 3 4 5 6 7 8 9 10
setInterval ( ( ) => {
const now = Date. now ( ) ;
const end = now + 1000 ;
console. log ( now) ;
while ( Date. now ( ) < end) { }
console. log ( '本人未耗时1秒,要求间隔两秒执行' ) ;
} , 2000 ) ;
1 2 3 4 5 6 7 8
requestAnimationFrame是setInterval的升级版,解决了一些循环上的问题,会在浏览器重新加载指定代码之前执行代码块,从而允许动画以适当的帧速率运行 只有浏览器支持
在动画函数最后用requestAnimationFrame在下次重绘前调用自己 入参:Function,并传入了一个时间戳 function draw ( timeStamp ) {
console. log ( timeStamp)
requestAnimationFrame ( draw) ;
}
const now = Date. now ( ) ;
draw ( now) ;
1 2 3 4 5 6 7 8 9 10
const timer;
function draw ( timeStamp ) {
timer = requestAnimationFrame ( draw) ;
}
draw ( ) ;
cancelAnimationFrame ( timer) ;
1 2 3 4 5 6 7
nodeJS的异步 文档 (opens new window) nodejs只有轮询机制 process.nextTick
为这一次轮询的末尾执行 setImmediate
为下一次轮询开始执行 setTimeout
加到下次轮询执行队列内 console. log ( 1 ) ;
setTimeout ( ( ) => console. log ( 5 ) ) ;
setImmediate ( ( ) => console. log ( 4 ) ) ;
process. nextTick ( ( ) => console. log ( 3 ) ) ;
console. log ( 2 ) ;
1 2 3 4 5 6 7
方法 Promise 构造器
调用:new Promise((resolve, reject) => {}) 入参:Function 返回:Promise对象 状态
Promise有三种状态,不可逆转
pending -> fulfilled
pending -> rejected
接收参数是一个函数,函数有resolve和reject两个入参 new Promise/then/catch返回的是Promise,所以能链式调用 无论返回的是什么都会被包装成promise,undefined也是 一个promise返回了异常,调用本Promise后面的catch函数,catch函数还可以返回fulfilling的promise resolve()触发状态 -> fulfilling,将then()方法的入参函数推入微任务队列 reject()触发状态 -> rejected,将catch()方法推入微任务队列,且不再执行后续的then方法,因为不可逆 new Promise ( ( resolve, reject ) => {
console. log ( '同步任务' ) ;
resolve ( ) ;
console. log ( '同步任务2' ) ;
} ) . then ( res => console. log ( '默认返回一个已满足的promise' ) )
. then ( res => Promise. reject ( '状态转换成了rejected,后面的then不执行,直接跳到catch' ) )
. then ( _ => console. log ( '不执行' ) )
. catch ( err => console. log ( err) ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
new Promise ( ( resolve, reject ) => {
resolve ( '111' ) ;
reject ( '222' ) ;
console. log ( '333' ) ;
} ) . then ( res => console. log ( res) )
. catch ( err => console. log ( err) ) ;
1 2 3 4 5 6 7 8 9 10 11
一个promise变成了rejected,调用本Promise后面的catch函数,catch函数还可以返回fulfilling的promise new Promise ( ( resolve, reject ) => {
resolve ( '111' ) ;
} ) . then ( res => Promise. reject ( '失败的promise1' ) )
. then (
res => { } ,
err => {
console. log ( err)
return Promise. reject ( '失败的promise2' )
}
) . then (
res => { } ,
err => {
console. log ( err) ;
return new Promise ( resolve => resolve ( '成功的promise3' ) ) ;
}
) . then ( res => console. log ( res) ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
then
作用:Promise对象状态发生改变后的回调函数 定义:promise.then(res => {}[, err => {}]) 入参:Function[, Function] 返回:Promise tip:第一个入参是状态变为fulfilling的回调,第二个入参是状态变为rejected的回调 const promise = new Promise ( ( resolve, reject ) => {
resolve ( '满足1' ) ;
} ) . then ( res => {
console. log ( res) ;
return Promise. reject ( '拒绝2' )
} ) . then ( res => { } , err => console. log ( err) ) ;
1 2 3 4 5 6 7 8 9 10
new Promise ( resolve => resolve ( 1 ) )
. then ( res => Promise. resolve ( res + 1 ) )
. then ( res => Promise. resolve ( res + 1 ) )
. then ( res => Promise. resolve ( res + 1 ) )
. then ( res => Promise. resolve ( res + 1 ) )
. then ( res => console. log ( res) )
1 2 3 4 5 6
返回值一定是一个Promise,如果不是,会被包装成一个已resolve的Promise,值是return的值 new Promise ( resolve => resolve ( '同步代码' ) )
. then ( res => console. log ( res) )
. then ( res => console. log ( res) )
1 2 3
catch
作用:promise状态变rejected或抛出异常时调用 定义:promise.catch(err => {}) 定义2:promise.then(undefined, err => {}) 入参:Function 返回:Promise new Promise ( ( resolve, reject ) => reject ( 'Some error!' ) )
. then ( undefined , err => console. log ( err) ) ;
Promise. reject ( 'Some error 2!' )
. catch ( err => console. log ( err) ) ;
1 2 3 4 5
new Promise ( resolve => { throw 'error!' } )
. catch ( err => console. log ( err) ) ;
1 2
返回值一定是一个Promise,如果不是,会被包装成一个已fulfilling的promise new Promise ( resolve => { throw 'error!' } )
. catch ( err => console. log ( err) )
. then ( res => console. log ( '---' ) )
1 2 3
finally
作用:无论状态变成什么,最后都会执行的一个回调 定义:promise.finally(callback) 入参:Function 返回:Promise new Promise ( resolve => {
resolve ( '11' ) ;
} ) . then ( res => console. log ( res) )
. finally ( arg => console. log ( arg) )
1 2 3 4
Promise. resolve ( '11' )
. then ( res => console. log ( res) )
. then ( _ => { throw '22' } )
. catch ( err => {
console. log ( err) ;
} )
. then ( _ => { throw '33' } )
. then ( _ => { throw '44' } )
. catch ( err => {
console. log ( err) ;
} )
. finally ( ( ) => console. log ( '一定会执行' ) ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
静态方法 all
作用:将多个Promise包装成一个Promise 调用:Promise.all(promiseArr) 入参:Iterator 返回:Promise
.then的回调函数接受的参数是所有promise的resolve结果数组(有序) .catch是任意一个入参promise被reject或抛出错误就会执行 then接收的是所有promise的resolve结果数组 const p1 = new Promise ( resolve => setTimeout ( ( ) => resolve ( '1' ) , 1000 ) ) ;
const p2 = new Promise ( resolve => setTimeout ( ( ) => resolve ( '2' ) , 2000 ) ) ;
const all = Promise. all ( [ p1, p2] ) ;
all. then ( res => console. log ( res) ) ;
1 2 3 4 5
任意一个入参promise抛出异常或reject都会导致整体的包装Promise被reject reject接受的参数是抛出的异常或reject的值 const p1 = new Promise ( resolve => setTimeout ( ( ) => resolve ( '1' ) , 1000 ) ) ;
const p2 = new Promise ( ( resolve, reject ) => setTimeout ( ( ) => reject ( '2' ) , 2000 ) ) ;
const all = Promise. all ( [ p1, p2] ) ;
all. then ( undefined , err => console. log ( err) ) ;
1 2 3 4 5
const p1 = new Promise ( resolve => setTimeout ( ( ) => resolve ( '1' ) , 1000 ) ) ;
const p2 = new Promise ( ( resolve, reject ) => setTimeout ( ( ) => { throw 'err' } , 2000 ) ) ;
const all = Promise. all ( [ p1, p2] ) ;
all. then ( undefined , err => console. log ( err) ) ;
1 2 3 4 5
any 未完全支持
环境 版本 chrome 85 node 15.0
any
作用:接收一个可迭代的promise集合,只返回第一个成功的promise结果 race
作用:接收一个promise集合,采用第一个状态改变的promise的结果作为整体的结果 定义:Promise.race(promiseArr) 入参:Iterator 返回:Promise 只要第一个出结果的,但是其他promise还是会执行完毕
const p1 = new Promise ( resolve => setTimeout ( ( ) => resolve ( '1' ) , 2000 ) ) ;
const p2 = new Promise ( resolve => setTimeout ( ( ) => resolve ( '2' ) , 500 ) ) ;
const race = Promise. race ( [ p1, p2] ) ;
race. then ( res => console. log ( res) ) ;
1 2 3 4 5 6
const p1 = new Promise ( resolve => setTimeout ( ( ) => resolve ( '1' ) , 2000 ) ) ;
const p2 = new Promise ( ( resolve, reject ) => setTimeout ( ( ) => reject ( 'err' ) , 500 ) ) ;
const race = Promise. race ( [ p1, p2] ) ;
race. catch ( err => console. log ( err) ) ;
1 2 3 4 5
allSettled
作用:收集一个promise集合,等所有的promise状态都发生转变了,将所有的promise结果包装起来返回无论是成功还是失败 定义:Promise.allSettled(promiseArr) 入参:Iterator 返回:Promise tip:和all区别:all有失败就只返回失败的结果 tip:选择:promise之间有相互依赖选all;无依赖,只是并发选allSettled const p1 = new Promise ( resolve => setTimeout ( ( ) => resolve ( '1' ) , 1000 ) ) ;
const p2 = new Promise ( resolve => setTimeout ( ( ) => resolve ( '2' ) , 2000 ) ) ;
const allSettled = Promise. allSettled ( [ p1, p2] ) ;
allSettled. then ( res => console. log ( res) ) ;
1 2 3 4 5 6 7 8 9 10 11
无论入参promise集合有多少成功失败,allSettled最后都是返回成功,并返回所有的状态 const p1 = new Promise ( resolve => setTimeout ( ( ) => resolve ( '1' ) , 1000 ) ) ;
const p2 = new Promise ( ( resolve, reject ) => setTimeout ( ( ) => reject ( 'err' ) , 2000 ) ) ;
const allSettled = Promise. allSettled ( [ p1, p2] ) ;
allSettled. then ( res => console. log ( res) ) ;
1 2 3 4 5 6 7 8 9 10 11
resolve
作用:返回一个以给定值解析后的Promise对象 定义:Promise.resolve(promise) 入参:[Promise | thenable](当做本promise.resovle的返回) | any(被当做resolve的值) 返回:Promise 入参
入参有两种情况
Promise/thenble other Promise/thenable会被当做Promise.resolve(promise) 的返回处理。也就是直接入参当做返回值 other会被当做本Promise成功时resolve的值。也就是返回一个新的Promise,resolve(other) const p = new Promise ( resolve => setTimeout ( ( ) => resolve ( '1' ) , 2000 ) ) ;
const resolve = Promise. resolve ( p) ;
console. log ( resolve === p) ;
resolve. then ( res => console. log ( res) ) ;
1 2 3 4 5 6 7
const p = ( ) => console. log ( '非Promise' ) ;
const resolve = Promise. resolve ( p) ;
console. log ( resolve === p) ;
resolve. then ( res => console. log ( res) ) ;
1 2 3 4 5 6 7
Promise.resolve(value)实际上就是new Promise(resolve => resolve(value)) 对比1:Promise.resolve(value) 的 value 为 Promise。
p2发现入参p1是promise,就将p1推入了微任务队列,而p1存的是p1这一行的promise.then返回的pending状态的promise ,也就是要等p1行的promise.then执行完后才能将p2后续的then推入微任务队列
const p1 = new Promise ( resolve => resolve ( 1 ) ) . then ( res => console. log ( res) ) ;
const p2 = Promise. resolve ( p1) . then ( res => console. log ( 2 ) ) ;
const p3 = new Promise ( resolve => resolve ( 3 ) ) . then ( res => console. log ( res) ) ;
console. log ( '同步' ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
Promise.resolve入参是同步代码时,那么会直接resolve(value),将自己的then推入微任务队列
const p1 = new Promise ( resolve => {
console. log ( 'p1代码' )
resolve ( 1 )
} ) . then ( res => console. log ( res) ) ;
Promise. resolve ( 2 ) . then ( res => console. log ( res) ) ;
const p3 = new Promise ( resolve => resolve ( 3 ) ) . then ( res => console. log ( res) ) ;
console. log ( '同步' ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
reject
作用:reject返回一个带有拒绝原因的Promise对象 定义:Promise.reject(reason) 入参:any 返回:Promise const p = Promise. reject ( 'err' ) ;
p. catch ( reason => console. log ( reason) ) ;
1 2