前端筆試題(異步專項(xiàng)練習(xí))

第一題:閱讀下面代碼,我們只考慮瀏覽器環(huán)境下的輸出結(jié)果捏萍,寫出它們結(jié)果打印的先后順序罢维,并分析出原因淹仑。

console.log("AAAA");
setTimeout(() => console.log("BBBB"), 1000);
const start = new Date();
while (new Date() - start < 3000) {}
console.log("CCCC");
setTimeout(() => console.log("DDDD"), 0);
new Promise((resolve, reject) => {
        console.log("EEEE");
        foo.bar(100);
    })
    .then(() => console.log("FFFF"))
    .then(() => console.log("GGGG"))
    .catch(() => console.log("HHHH"));
console.log("IIII");

答案解析:
這道題考察重點(diǎn)是 js異步執(zhí)行 宏任務(wù) 微任務(wù)。
一開始代碼執(zhí)行肺孵,輸出AAAA. 1
第二行代碼開啟一個(gè)計(jì)時(shí)器t1(一個(gè)稱呼)攻人,這是一個(gè)異步任務(wù)且是宏任務(wù),需要等到1秒后提交悬槽。
第四行是個(gè)while語句怀吻,需要等待3秒后才能執(zhí)行下面的代碼,這里有個(gè)問題,就是3秒后上一個(gè)計(jì)時(shí)器t1的提交時(shí)間已經(jīng)過了初婆,但是線程上的任務(wù)還沒有執(zhí)行結(jié)束蓬坡,所以暫時(shí)不能打印結(jié)果猿棉,所以它排在宏任務(wù)的最前面了。
第五行又輸出CCCC
第六行又開啟一個(gè)計(jì)時(shí)器t2(稱呼)屑咳,它提交的時(shí)間是0秒(其實(shí)每個(gè)瀏覽器器有默認(rèn)最小時(shí)間的萨赁,暫時(shí)忽略),但是之前的t1任務(wù)還沒有執(zhí)行兆龙,還在等待杖爽,所以t2就排在t1的后面。(t2排在t1后面的原因是while造成的)都還需要等待紫皇,因?yàn)榫€程上的任務(wù)還沒執(zhí)行完畢慰安。
第七行new Promise將執(zhí)行promise函數(shù),它參數(shù)是一個(gè)回調(diào)函數(shù)聪铺,這個(gè)回調(diào)函數(shù)內(nèi)的代碼是同步的化焕,它的異步核心在于resolve和reject,同時(shí)這個(gè)異步任務(wù)在任務(wù)隊(duì)列中屬于微任務(wù)铃剔,是優(yōu)先于宏任務(wù)執(zhí)行的撒桨,(不管宏任務(wù)有多急,反正我是VIP)键兜。所以先直接打印輸出同步代碼EEEE凤类。第九行中的代碼是個(gè)不存在的對象,這個(gè)錯(cuò)誤要拋給reject這個(gè)狀態(tài)普气,也就是catch去處理谜疤,但是它是異步的且是微任務(wù),只有等到線程上的任務(wù)執(zhí)行完畢棋电,立馬執(zhí)行它茎截,不管宏任務(wù)(計(jì)時(shí)器,ajax等)等待多久了赶盔。
第十四行企锌,這是線程上的最后一個(gè)任務(wù),打印輸出 IIII
我們先找出線程上的同步代碼于未,將結(jié)果依次排列出來:AAAA CCCC EEEE IIII
然后我們再找出所有異步任務(wù)中的微任務(wù) 把結(jié)果打印出來 HHHH
最后我們再找出異步中的所有宏任務(wù)撕攒,這里t1排在前面t2排在后面(這個(gè)原因是while造成的),輸出結(jié)果順序是 BBBB DDDD
所以綜上 結(jié)果是 AAAA CCCC EEEE IIII HHHH BBBB DDDD

第二題:閱讀下面代碼烘浦,我們只考慮瀏覽器環(huán)境下的輸出結(jié)果抖坪,寫出它們結(jié)果打印的先后順序,并分析出原因闷叉。

async function async1() {
    console.log("AAAA");
    async2();
    console.log("BBBB");
}
async function async2() {
    console.log("CCCC");
}
console.log("DDDD");
setTimeout(function () {
    console.log("FFFF");
}, 0);
async1();
new Promise(function (resolve) {
    console.log("GGGG");
    resolve();
}).then(function () {
    console.log("HHHH");
});
console.log("IIII");

答案解析:
這道題考察重點(diǎn)是 js異步執(zhí)行 宏任務(wù) 微任務(wù).
這道題的坑就在于 async中如果沒有await擦俐,那么它就是一個(gè)純同步函數(shù)。
這道題的起始代碼在第9行握侧,輸出DDDD
第10行計(jì)時(shí)器開啟一個(gè)異步任務(wù)t1(一個(gè)稱呼)蚯瞧,這個(gè)任務(wù)且為宏任務(wù)嘿期。
第13行函數(shù)async1執(zhí)行,這個(gè)函數(shù)內(nèi)沒有await 所以它其實(shí)就是一個(gè)純同步函數(shù)埋合,打印輸出AAAA,
在async1中執(zhí)行async2函數(shù)备徐,因?yàn)閍sync2的內(nèi)部也沒有await,所以它也是個(gè)純同步函數(shù)甚颂,打印輸出CCCC
緊接著打印輸出BBBB蜜猾。
第14行new Promise執(zhí)行里面的代碼也是同步的,所以打印輸出GGGG,resolve()調(diào)用的時(shí)候開啟一個(gè)異步任務(wù)t2(一個(gè)稱呼),且這個(gè)任務(wù)t2是微任務(wù)振诬,它的執(zhí)行交給then()中的第一個(gè)回調(diào)函數(shù)執(zhí)行蹭睡,且優(yōu)先級高于宏任務(wù)(t1)執(zhí)行。
第20行打印輸出IIII,此時(shí)線程上的同步任務(wù)全部執(zhí)行結(jié)束贷揽。
在執(zhí)行任務(wù)隊(duì)列中的異步任務(wù)時(shí)棠笑,微任務(wù)優(yōu)先于宏任務(wù)執(zhí)行梦碗,所以先執(zhí)行微任務(wù) t2 打印輸出 HHHH,然后執(zhí)行宏任務(wù) t1 打印輸出 FFFF
所以綜上 結(jié)果輸出是 DDDD AAAA CCCC BBBB GGGG IIII HHHH FFFF

第三題:寫出以下代碼的運(yùn)行結(jié)果禽绪,共有七小問。

問題1

async function t1() {
  let a = await "mango";
  console.log(a);
}
t1()

答案解析:
await是一個(gè)表達(dá)式洪规,如果后面不是一個(gè)promise對象印屁,就直接返回對應(yīng)的值。
所以問題1可以理解為

async function t1() {
 let a = "mango";
 console.log(a);//mango
}
t1()

問題2

async function t2() {
  let a = await new Promise((resolve) => {});
  console.log(a);//
}
t2()

答案解析:
await后面如果跟一個(gè)promise對象斩例,await將等待這個(gè)promise對象的resolve狀態(tài)的值value雄人,且將這個(gè)值返回給前面的變量,此時(shí)的promise對象的狀態(tài)是一個(gè)pending狀態(tài)念赶,沒有resolve狀態(tài)值础钠,所以什么也打印不了。

問題3

async function t3() {
  let a = await new Promise((resolve) => {
    resolve();
  });
  console.log(a);//undefined
}
t3()

答案解析:
await后面如果跟一個(gè)promise對象叉谜,await將等待這個(gè)promise對象的resolve狀態(tài)的值value旗吁,且將這個(gè)值返回給前面的變量,此時(shí)的promise對象的狀態(tài)是一個(gè)resolve狀態(tài)停局,但是它的狀態(tài)值是undefined很钓,所以打印出undefined。

問題4

async function t4() {
  let a = await new Promise((resolve) => {
    resolve("hello");
  });
  console.log(a);//hello
}
t4()

答案解析:
await后面如果跟一個(gè)promise對象董栽,await將等待這個(gè)promise對象的resolve狀態(tài)的值码倦,且將這個(gè)值返回給前面的變量,此時(shí)的promise對象的狀態(tài)是一個(gè)resolve狀態(tài)锭碳,它的狀態(tài)值是hello袁稽,所以打印出hello。

問題5

async function t5() {
  let a = await new Promise((resolve) => {
    resolve("hello");
  }).then(() => {
    return "lala";
  });
  console.log(a);//lala
}
t5()

答案解析:
await后面如果跟一個(gè)promise對象擒抛,await將等待這個(gè)promise對象的resolve狀態(tài)的值推汽,且將這個(gè)值返回給前面的變量蝗柔,此時(shí)的promise對象的狀態(tài)是一個(gè)resolve狀態(tài),它的狀態(tài)值是hello民泵,緊接著后面又執(zhí)行了一個(gè)then方法癣丧,then方法又會返回一個(gè)全新的promise對象,且這個(gè)then方法中的返回值會作為這個(gè)全新的promise中resolve的值栈妆,所以最終的結(jié)果是lala胁编。

問題6

async function t6() {
  let a = await fn().then((res)=>{return res})
  console.log(a);//undefined
}
async function fn(){
    await new Promise((resolve)=>{
        resolve("mango")
    })
}
t6()

答案解析:
async函數(shù)執(zhí)行返回一個(gè)promise對象,且async函數(shù)內(nèi)部的返回值會當(dāng)作這個(gè)promise對象resolve狀態(tài)的值

async function fn() {
  return "la";
}
var p = fn();
console.log(p); //Promise {<resolved>: "la"}
            //__proto__: Promise
                       //[[PromiseStatus]]: "resolved"
                      //[[PromiseValue]]: "la"

首先考慮 fn() 執(zhí)行返回一個(gè)promise對象,因?yàn)閒n執(zhí)行沒有返回值鳞尔,所以這個(gè)promise對象的狀態(tài)resolve的值是undefined嬉橙,且將這個(gè)undefined當(dāng)作下一個(gè)then中回調(diào)函數(shù)的參數(shù),所以打印的結(jié)果是undefined

問題7

async function t7() {
  let a = await fn().then((res)=>{return res})
  console.log(a);
}
async function fn(){
    await new Promise((resolve)=>{
        resolve("mango")
    })
    return "lala"
}
t7()

答案解析:
首先考慮 fn() 執(zhí)行返回一個(gè)promise對象寥假,因?yàn)?code>fn()執(zhí)行有返回值lala市框,所以這個(gè)promise對象的狀態(tài)resolve的值是lala,且將這個(gè)lala當(dāng)作下一個(gè)then中回調(diào)函數(shù)的參數(shù)糕韧,所以打印的結(jié)果是lala枫振。

注意細(xì)節(jié)

  • async函數(shù)執(zhí)行的返回結(jié)果是一個(gè)promise對象,這個(gè)函數(shù)的返回值是這個(gè)promise狀態(tài)值resolve的值

  • await后面如果不是一個(gè)promise對象萤彩,將直接返回這個(gè)值

  • await后面如果是一個(gè)promise對象粪滤,將會把這個(gè)promise的狀態(tài)resolve的值返回出去。

    以上沒有考慮reject狀態(tài)雀扶。

第四題:談一談下面兩種寫法的區(qū)別:

//第一種
promise.then((res) => {
    console.log('then:', res)
}).catch((err) => {
    console.log('catch:', err)
})
//第二種
promise.then((res) => {
    console.log('then:', res)
}, (err) => {
    console.log('catch:', err)
})

答案解析:
第一種catch方法可以捕獲到catch之前的整條鏈路上拋出的異常杖小。
第二種then方法的第二個(gè)參數(shù)捕獲的異常依賴于上一個(gè)Promise對象的執(zhí)行結(jié)果。
promise.then(seccessCb,fallCb)接收兩個(gè)函數(shù)作為參數(shù)愚墓,來處理上一個(gè)promise對象的結(jié)果予权。then方法返回的是promise對象。
第一種鏈?zhǔn)綄懛ɡ瞬幔褂胏atch扫腺,相當(dāng)于給前一個(gè)then方法返回的promise注冊回調(diào),可以捕獲到前面then沒有被處理的異常议经。
第二種是回調(diào)的寫法斧账,僅為上一個(gè)promise注冊異常回調(diào)煞肾。
如果是promise內(nèi)部報(bào)錯(cuò)reject拋出錯(cuò)誤后咧织,then的第二個(gè)參數(shù)就能捕獲到,如果then的第二個(gè)參數(shù)不存在籍救,則catch方法會捕獲到习绢。
如果是then的第一個(gè)參數(shù)函數(shù)resolve中拋出了異常,即成功回調(diào)函數(shù)出現(xiàn)異常后,then的第二個(gè)參數(shù)reject捕獲不到闪萄,但是catch方法可以捕獲到梧却。

第五題:根據(jù)題目寫出符合要求的代碼

var urls = [
  'http://jsonplaceholder.typicode.com/posts/1',
  'http://jsonplaceholder.typicode.com/posts/2', 
  'http://jsonplaceholder.typicode.com/posts/3', 
  'http://jsonplaceholder.typicode.com/posts/4',
  'http://jsonplaceholder.typicode.com/posts/5', 
  'http://jsonplaceholder.typicode.com/posts/6', 
  'http://jsonplaceholder.typicode.com/posts/7', 
  'http://jsonplaceholder.typicode.com/posts/8',
  'http://jsonplaceholder.typicode.com/posts/9', 
  'http://jsonplaceholder.typicode.com/posts/10'
]

function loadDate (url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.onload = function () {
      resolve(xhr.responseText)
    }
    xhr.open('GET', url)
    xhr.send()
  })
}

在 urls 數(shù)組中存放了 10 個(gè)接口地址。同時(shí)還定義了一個(gè) loadDate 函數(shù)败去,這個(gè)函數(shù)接受一個(gè) url 參數(shù)放航,返回一個(gè) Promise 對象,該 Promise 在接口調(diào)用成功時(shí)返回 resolve圆裕,失敗時(shí)返回 reject广鳍。
要求:任意時(shí)刻,同時(shí)下載的鏈接數(shù)量不可以超過 3 個(gè)吓妆。 試寫出一段代碼實(shí)現(xiàn)這個(gè)需求赊时,要求盡可能快速地將所有接口中的數(shù)據(jù)得到。

答案解析:
按照題意我們可以這樣做行拢,首先并發(fā)請求 3 個(gè) url 中的數(shù)據(jù)祖秒,當(dāng)其中一條 url 請求得到數(shù)據(jù)后,立即發(fā)起對一條新 url 上數(shù)據(jù)的請求舟奠,我們要始終讓并發(fā)數(shù)保持在 3 個(gè)竭缝,直到所有需要加載數(shù)據(jù)的 url 全部都完成請求并得到數(shù)據(jù)。
用 Promise 實(shí)現(xiàn)的思路就是鸭栖,首先并發(fā)請求3個(gè) url 歌馍,得到 3 個(gè) Promise 握巢,然后組成一個(gè)叫 promises 的數(shù)組晕鹊。再不斷的調(diào)用 Promise.race 來返回最快改變狀態(tài)的 Promise ,然后從數(shù)組promises中刪掉這個(gè) Promise 對象暴浦,再加入一個(gè)新的 Promise溅话,直到所有的 url 被取完,最后再使用 Promise.all 來處理一遍數(shù)組promises中沒有改變狀態(tài)的 Promise歌焦。

var urls = [
  'http://jsonplaceholder.typicode.com/posts/1',
  'http://jsonplaceholder.typicode.com/posts/2', 
  'http://jsonplaceholder.typicode.com/posts/3', 
  'http://jsonplaceholder.typicode.com/posts/4',
  'http://jsonplaceholder.typicode.com/posts/5', 
  'http://jsonplaceholder.typicode.com/posts/6', 
  'http://jsonplaceholder.typicode.com/posts/7', 
  'http://jsonplaceholder.typicode.com/posts/8',
  'http://jsonplaceholder.typicode.com/posts/9', 
  'http://jsonplaceholder.typicode.com/posts/10'
]

function loadDate (url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest()
    xhr.onload = function () {
      resolve(xhr.responseText)
    }
    xhr.open('GET', url)
    xhr.send()
  })
}

function limitLoad(urls, handler, limit) {
    // 對數(shù)組進(jìn)行一個(gè)拷貝
    const sequence = [].concat(urls)
    let promises = [];

    //實(shí)現(xiàn)并發(fā)請求達(dá)到最大值
    promises = sequence.splice(0, limit).map((url, index) => {
        // 這里返回的 index 是任務(wù)在數(shù)組 promises 的腳標(biāo)
        //用于在 Promise.race 后找到完成的任務(wù)腳標(biāo)
        return handler(url).then(() => {
            return index
        }); 
    });

    // 利用數(shù)組的 reduce 方法來以隊(duì)列的形式執(zhí)行
    return sequence.reduce((last, url, currentIndex) => {
        return last.then(() => {
            // 返回最快改變狀態(tài)的 Promise
            return Promise.race(promises)
        }).catch(err => {
            // 這里的 catch 不僅用來捕獲前面 then 方法拋出的錯(cuò)誤
            // 更重要的是防止中斷整個(gè)鏈?zhǔn)秸{(diào)用
            console.error(err)
        }).then((res) => {
            // 用新的 Promise 替換掉最快改變狀態(tài)的 Promise
            promises[res] = handler(sequence[currentIndex]).then(
                () => { return res });
        })
    }, Promise.resolve()).then(() => {
        return Promise.all(promises)
    })
    
}
limitLoad(urls, loadDate, 3)

/*
因?yàn)?loadDate 函數(shù)也返回一個(gè) Promise
所以當(dāng) 所有圖片加載完成后可以繼續(xù)鏈?zhǔn)秸{(diào)用

limitLoad(urls, loadDate, 3).then(() => {
    console.log('所有url數(shù)據(jù)請求成功');
}).catch(err => {
    console.error(err);
})
*/
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末飞几,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子独撇,更是在濱河造成了極大的恐慌屑墨,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纷铣,死亡現(xiàn)場離奇詭異卵史,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)搜立,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門以躯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事忧设〉蟊辏” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵址晕,是天一觀的道長膀懈。 經(jīng)常有香客問我,道長谨垃,這世上最難降的妖魔是什么吏砂? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮乘客,結(jié)果婚禮上狐血,老公的妹妹穿的比我還像新娘。我一直安慰自己易核,他們只是感情好匈织,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著牡直,像睡著了一般缀匕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上碰逸,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天乡小,我揣著相機(jī)與錄音,去河邊找鬼饵史。 笑死满钟,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的胳喷。 我是一名探鬼主播湃番,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吭露!你這毒婦竟也來了吠撮?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤讲竿,失蹤者是張志新(化名)和其女友劉穎泥兰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體题禀,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鞋诗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了投剥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片师脂。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吃警,到底是詐尸還是另有隱情糕篇,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布酌心,位于F島的核電站拌消,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏安券。R本人自食惡果不足惜墩崩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侯勉。 院中可真熱鬧鹦筹,春花似錦、人聲如沸址貌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽练对。三九已至遍蟋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間螟凭,已是汗流浹背虚青。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留螺男,地道東北人棒厘。 一個(gè)月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像烟号,于是被迫代替她去往敵國和親绊谭。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 以下題目是根據(jù)網(wǎng)上多份面經(jīng)收集而來的汪拥,題目相同意味著被問的頻率比較高(x3表示有三份面經(jīng)被問),有問題歡迎留言討論...
    Aniugel閱讀 2,362評論 0 7
  • 1.引言這一篇文章準(zhǔn)備寫一下異步中的generator和async函數(shù)篙耗,下一篇文章準(zhǔn)備寫js中的運(yùn)行機(jī)制迫筑,也就是e...
    WEB前端含光閱讀 885評論 0 0
  • client,page和screen的區(qū)別宗弯? clientX脯燃,clientY是觸摸點(diǎn)相對于viewport視口x,...
    change_22fa閱讀 1,640評論 1 1
  • css相關(guān) 1. 萬能居中 1.margin: 0 auto;水平 2.text-align: center;水平...
    chaocc閱讀 954評論 0 2
  • 黑色的海島上懸著一輪又大又圓的明月辕棚,毫不嫌棄地把溫柔的月色照在這寸草不生的小島上。一個(gè)少年白衣白發(fā),悠閑自如地倚坐...
    小水Vivian閱讀 3,102評論 1 5