js事件循环——看输出顺序
js事件循環:
由于js是單線程的,同一時間只能干一件事情,當期宿主環境為瀏覽器時,若一個任務耗時過長會導致頁面阻塞。因此有了js事件循環機制,它將任務分成同步任務和異步任務,同步任務在主線程不斷執行,異步任務進入任務隊列,當同步任務執行完主棧為空時,就去任務隊列讀取異步任務執行,這個不斷循環的過程稱為事件循環。
1.promise:
微任務,進入微任務隊列,在本輪事件循環中執行,例如:
new Promise(function(resolve) {console.log('promise'); }).then(function() {console.log('then'); })new Promise立即執行,then函數分發到微任務Event Queue。需要注意的幾點
1.promise.then用于指定promise對象成功或者失敗的回調函數,所以僅當promise對象為fulfilled或者rejected狀態才會去執行,否則不執行:
const promise1 = new Promise((resolve, reject) => {setTimeout(() => {resolve('success')}, 1000) }) const promise2 = promise1.then(() => {throw new Error('error!!!') })console.log('promise1', promise1) console.log('promise2', promise2)setTimeout(() => {console.log('promise1', promise1) console.log('promise2', promise2) }, 2000)執行結果是:
對于這里為什么throw錯誤之后代碼還能繼續執行:因為Promise對象和Promise.then方法內部其實自帶了try catch,當同步代碼發生運行時錯誤時,會自動將錯誤對象作為值reject,捕獲錯誤,所以代碼還能繼續往下運行。此題promise1.then執行后返回一個被reject的promise對象。
?2.Promise.then返回值是一個promise對象,以實現鏈式調用。
(1)當回調函數返回值是一個promise對象時,得到新的promise值作為返回的promise結果值;
(2)若返回值是一個一般值時,將這個值作為返回的promise結果值,相當于resolve(x),可繼續向下一個then中的回調去傳遞;
(3)若不顯式的 return 則會 return 一個以 undefined 值 resolve 的 Promise 對象
Promise.resolve(1).then((res) => {console.log(res)return 2}).catch((err) => {return 3}).then((res) => {console.log('執行了');console.log(res)})依次輸出:1,執行了,23.promise有pending,fulfilled,rejected三種狀態,狀態只能被改變一次
const promise = new Promise((resolve, reject) => {resolve('success1')reject('error')resolve('success2') }) promise .then((res) => {console.log('then: ', res) }) .catch((err) => {console.log('catch: ', err) })代碼輸出:success1,then:success12.setTimeout:
宏任務,進入宏任務隊列,在下一輪事件循環執行,例如:
setTimeout(() => {task(); },3000) console.log('執行console');//執行console //task()3.async/await:
async聲明異步函數,返回的是promise對象,如果返回的不是promise會自動用Promise.resolve()包裝,例如:
async function test(){return 'test' } test()返回值為Promise{<resolved>:"test"}await表示等待右側表達式的結果,這個結果是promise對象或者其他值。如果是promise對象則等到的結果為Promise狀態變為fulfilled之后resolve的參數,如果不是promise對象則直接將該值作為等到的值。
function test(){return new Promise(resolve=>{setTimeout(()=>resolve('test'),2000);}); }const result=await test(); console.log(result); console.log('end');console.log('end')會等到兩秒之后執行由于await會阻塞后面的代碼,所以為了避免造成阻塞,await 必須用在 async 函數中,async 函數調用不會造成阻塞。
function test(){return new Promise(resolve=>{setTimeout(()=>resolve('test'),2000);}); }async function test2(){const result=await test();console.log(result); } test2(); console.log('end');先執行console.log('end'),兩秒后執行console.log('test')async/await的執行順序:await是一個讓出線程的標志,await后面的函數會先執行一遍,然后就會跳出整個async函數(這里相當于從右向左執行,先執行await后面的東西,碰到await就讓出線程阻塞代碼),先執行async外面的同步代碼,同步代碼執行完,再回到async內部,繼續執行await后面的代碼。
async function test1 () {console.log ('start test1') ;console.log ( await test2 () ) ;console.log ('end test1') ; } async function test2 () {console.log ('test2') ;return await 'return test2 value' } test1 () ; console.log ('start async') ; setTimeout ( () => {console.log ('setTimeout') ; }, 0) ; new Promise ( (resolve, reject) => {console.log ('promise1') ;resolve () ; }) .then ( () =>{console.log ('promise2') ;}) ; console.log ('end async') ;?上述代碼輸出:
1.首先執行宏任務,執行test1函數,console.log('start test1');遇到await,執行console.log('test2')
2.執行console.log('start async');
3.遇到setTimeout進入宏任務隊列
4.執行new Promise,打印promise1,.then進入微任務隊列
5.執行console.log('end async');
6.test1()外面的同步代碼執行完畢,回到test1(),執行完console.log(await test2())返回Promise{<resolved>:'return test2 value'},推入微任務隊列
7.此時第一個宏任務結束,執行所有的微任務,因為微任務隊列先進先出,所以先執行console.log('promise2'),后執行console.log('return test2 value')
8.執行test2完成后,后面的代碼不再阻塞,執行console.log('end test1');
9.執行下個宏任務,即執行console.log('setTimeout');
補充:如果去掉test2中return 后面的await,則順序變為
因為return后面的await阻塞了promise的執行,此時test2()的返回值是一個pending狀態的promise,promise只有在resolve或者reject之后才會進入.then中執行回調,因此test2中返回的promise.then在new Promise.then之后進入微任務隊列。
總結
以上是生活随笔為你收集整理的js事件循环——看输出顺序的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: cmd打开时提示“系统找不到指定的路径”
- 下一篇: 大学四年总结(一)
