1. 程式人生 > >關於 ES6 中 Promise 的面試題

關於 ES6 中 Promise 的面試題

說明

最近在複習 Promise 的知識,所以就做了一些題,這裡挑出幾道題,大家一起看看吧。

題目一

const promise = new Promise((resolve, reject) => {
    console.log(1);
    resolve();
    console.log(2);
})

promise.then(() => {
    console.log(3);
})

console.log(4);

解析

首先 Promise 新建後立即執行,所以會先輸出 1,2,而 Promise.then() 內部的程式碼在 當次 事件迴圈的 結尾 立刻執行 ,所以會繼續輸出4,最後輸出3。

答案

1
2
4
3

題目二

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);
})

解析

resolve 函式將 Promise 物件的狀態從“未完成”變為“成功”

(即從 pending 變為 resolved),在非同步操作成功時呼叫,並將非同步操作的結果,作為引數傳遞出去;

reject 函式將 Promise 物件的狀態從“未完成”變為“失敗”(即從 pending 變為 rejected),在非同步操作失敗時呼叫,並將非同步操作報出的錯誤,作為引數傳遞出去。

而一旦狀態改變,就不會再變。
所以 程式碼中的reject('error'); 不會有作用。

Promise 只能 resolve 一次,剩下的呼叫都會被忽略。
所以 第二次的 resolve('success2'); 也不會有作用。

答案

then: success1

題目三

Promise.
resolve(1) .then(2) .then(Promise.resolve(3)) .then(console.log)

解析

Promise.resolve 方法的引數如果是一個原始值,或者是一個不具有 then 方法的物件,則 Promise.resolve 方法返回一個新的 Promise 物件,狀態為resolvedPromise.resolve 方法的引數,會同時傳給回撥函式。

then 方法接受的引數是函式,而如果傳遞的並非是一個函式,它實際上會將其解釋為 then(null),這就會導致前一個 Promise 的結果會穿透下面。

答案

1

題目四

紅燈三秒亮一次,綠燈一秒亮一次,黃燈2秒亮一次;如何讓三個燈不斷交替重複亮燈?(用Promse實現)三個亮燈函式已經存在:

function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}

解析

紅燈三秒亮一次,綠燈一秒亮一次,黃燈2秒亮一次,意思就是3秒,執行一次 red 函式,2秒執行一次 green 函式,1秒執行一次 yellow 函式,不斷交替重複亮燈,意思就是按照這個順序一直執行這3個函式,這步可以就利用遞迴來實現。

答案

function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}

var light = function (timmer, cb) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            cb();
            resolve();
        }, timmer);
    });
};

var step = function () {
    Promise.resolve().then(function () {
        return light(3000, red);
    }).then(function () {
        return light(2000, green);
    }).then(function () {
        return light(1000, yellow);
    }).then(function () {
        step();
    });
}

step();

題目五

實現 mergePromise 函式,把傳進去的陣列按順序先後執行,並且把返回的資料先後放到陣列 data 中。

const timeout = ms => new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve();
    }, ms);
});

const ajax1 = () => timeout(2000).then(() => {
    console.log('1');
    return 1;
});

const ajax2 = () => timeout(1000).then(() => {
    console.log('2');
    return 2;
});

const ajax3 = () => timeout(2000).then(() => {
    console.log('3');
    return 3;
});

const mergePromise = ajaxArray => {
    // 在這裡實現你的程式碼

};

mergePromise([ajax1, ajax2, ajax3]).then(data => {
    console.log('done');
    console.log(data); // data 為 [1, 2, 3]
});

// 要求分別輸出
// 1
// 2
// 3
// done
// [1, 2, 3]

解析

首先 ajax1 、ajax2、ajax3 都是函式,只是這些函式執行後會返回一個 Promise,按題目的要求我們只要順序執行這三個函式就好了,然後把結果放到 data 中,但是這些函式裡都是非同步操作,想要按順序執行,然後輸出 1,2,3並沒有那麼簡單,看個例子。

function A() {
    setTimeout(function () {
        console.log('a');
    }, 3000);
}

function B() {
    setTimeout(function () {
        console.log('b');
    }, 1000);
}

A();
B();

// b
// a

例子中我們是按順序執行的 AB 但是輸出的結果卻是 ba 對於這些非同步函式來說,並不會按順序執行完一個,再執行後一個。
這道題就是考用 Promise 控制非同步流程,我們要想辦法,讓這些函式,一個執行完之後,再執行下一個,看答案吧。

答案

// 儲存陣列中的函式執行後的結果
var data = [];

// Promise.resolve方法呼叫時不帶引數,直接返回一個resolved狀態的 Promise 物件。
var sequence = Promise.resolve();

ajaxArray.forEach(function (item) {
    // 第一次的 then 方法用來執行陣列中的每個函式,
    // 第二次的 then 方法接受陣列中的函式執行後返回的結果,
    // 並把結果新增到 data 中,然後把 data 返回。
    // 這裡對 sequence 的重新賦值,其實是相當於延長了 Promise 鏈
    sequence = sequence.then(item).then(function (res) {
        data.push(res);
        return data;
    });
})

// 遍歷結束後,返回一個 Promise,也就是 sequence, 他的 [[PromiseValue]] 值就是 data,
// 而 data(儲存陣列中的函式執行後的結果) 也會作為引數,傳入下次呼叫的 then 方法中。
return sequence;

題目六

以下程式碼最後輸出什麼?

const first = () => (new Promise((resolve, reject) => {
    console.log(3);
    let p = new Promise((resolve, reject) => {
        console.log(7);
        setTimeout(() => {
            console.log(5);
            resolve(6);
        }, 0)
        resolve(1);
    });
    resolve(2);
    p.then((arg) => {
        console.log(arg);
    });

}));

first().then((arg) => {
    console.log(arg);
});
console.log(4);

解析

這道題就其實和 Promise 的關係不太大,主要是需要理解 JS執行機制,才能很好的解決這道題,對於 JS 執行機制不瞭解的朋友推薦看看這篇文章

第一輪事件迴圈

先執行巨集任務,主script ,new Promise立即執行,輸出【3】,
執行 p 這個new Promise 操作,輸出【7】,
發現 setTimeout,將回調放入下一輪任務佇列(Event Queue),p 的 then,姑且叫做 then1,放入微任務佇列,發現 first 的 then,叫 then2,放入微任務佇列。執行console.log(4),輸出【4】,巨集任務執行結束。
再執行微任務,執行 then1,輸出【1】,
執行 then2,輸出【2】。
到此為止,第一輪事件迴圈結束。開始執行第二輪。

第二輪事件迴圈

先執行巨集任務裡面的,也就是 setTimeout 的回撥,輸出【5】。
resolve(6) 不會生效,因為 p 這個 Promise 的狀態一旦改變就不會在改變了。

答案

3
7
4
1
2
5 

題目七

有 8 個圖片資源的 url,已經儲存在陣列 urls 中(即urls = ['http://example.com/1.jpg', ...., 'http://example.com/8.jpg']),而且已經有一個函式 function loadImg,輸入一個 url 連結,返回一個 Promise,該 Promise 在圖片下載完成的時候 resolve,下載失敗則 reject。
但是我們要求,任意時刻,同時下載的連結數量不可以超過 3 個。
請寫一段程式碼實現這個需求,要求儘可能快速地將所有圖片下載完成。

var urls = ['https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg', 'https://www.kkkk1000.com/images/getImgData/gray.gif', 'https://www.kkkk1000.com/images/getImgData/Particle.gif', 'https://www.kkkk1000.com/images/getImgData/arithmetic.png', 'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif', 'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg', 'https://www.kkkk1000.com/images/getImgData/arithmetic.gif', 'https://www.kkkk1000.com/images/wxQrCode2.png'];
function loadImg(url) {
    return new Promise((resolve, reject) => {
        const img = new Image()
        img.onload = function () {
            console.log('一張圖片載入完成');
            resolve();
        }
        img.onerror = reject
        img.src = url
    })
};

解析

題目的意思是需要我們這麼做,先併發請求 3 張圖片,當一張圖片載入完成後,又會繼續發起一張圖片的請求,讓併發數保持在 3 個,直到需要載入的圖片都全部發起請求。

用 Promise 來實現就是,先併發請求3個圖片資源,這樣可以得到 3 個 Promise,組成一個數組,就叫promises 吧,然後不斷的呼叫 Promise.race 來返回最快改變狀態的 Promise,然後從陣列(promises)中刪掉這個 Promise 物件,再加入一個新的 Promise,直到全部的 url 被取完,最後再使用 Promise.all 來處理一遍陣列(promises)中沒有改變狀態的 Promise。

答案

var urls = ['https://www.kkkk1000.com/images/getImgData/getImgDatadata.jpg', 'https://www.kkkk1000.com/images/getImgData/gray.gif', 'https://www.kkkk1000.com/images/getImgData/Particle.gif', 'https://www.kkkk1000.com/images/getImgData/arithmetic.png', 'https://www.kkkk1000.com/images/getImgData/arithmetic2.gif', 'https://www.kkkk1000.com/images/getImgData/getImgDataError.jpg', 'https://www.kkkk1000.com/images/getImgData/arithmetic.gif', 'https://www.kkkk1000.com/images/wxQrCode2.png'];
function loadImg(url) {
    return new Promise((resolve, reject) => {
        const img = new Image()
        img.onload = function () {
            console.log('一張圖片載入完成');
            resolve();
        }
        img.onerror = reject
        img.src = url
    })
};

function limitLoad(urls, handler, limit) {
    // 對陣列做一個拷貝
    const sequence = [].concat(urls)
    let promises = [];

    //併發請求到最大數
    promises = sequence.splice(0, limit).map((url, index) => {
        // 這裡返回的 index 是任務在 promises 的腳標,用於在 Promise.race 之後找到完成的任務腳標
        return handler(url).then(() => {
            return index
        }); 
    });

    // 利用陣列的 reduce 方法來以佇列的形式執行
    return sequence.reduce((last, url, currentIndex) => {
        return last.then(() => {
            // 返回最快改變狀態的 Promise
            return Promise.race(promises)
        }).catch(err => {
            // 這裡的 catch 不僅用來捕獲 前面 then 方法丟擲的錯誤
            // 更重要的是防止中斷整個鏈式呼叫
            console.error(err)
        }).then((res) => {
            // 用新的 Promise 替換掉最快改變狀態的 Promise
            promises[res] = handler(sequence[currentIndex]).then(() => { return res });
        })
    }, Promise.resolve()).then(() => {
        return Promise.all(promises)
    })

}
limitLoad(urls, loadImg, 3)

/*
因為 limitLoad 函式也返回一個 Promise,所以當 所有圖片載入完成後,可以繼續鏈式呼叫

limitLoad(urls, loadImg, 3).then(() => {
    console.log('所有圖片載入完成');
}).catch(err => {
    console.error(err);
})
*/

總結

這幾道題,有考查 Promise 基礎知識的,也有考對 Promise 靈活運用的,如果這些題你都做的很好的話,那你對 Promise 的理解應該是不錯的了。

最後,如果文中有不足或者錯誤的地方,還請小夥伴們指出,萬分感謝。
如果覺得文章說的內容不夠,最後有與題目相關的文章,可以看看。

參考

相關推薦

關於 ES6 Promise試題

說明 最近在複習 Promise 的知識,所以就做了一些題,這裡挑出幾道題,大家一起看看吧。 題目一 const promise = new Promise((resolve, reject) => { console.log(1); re

es6promise ALL Race Resolve Reject finish的實現

success eject you his rip elf prot fail n) function mypromise(func){ this.statue = "pending"; this.data = null;

ES6 Promise 詳解

out 保持 type xxxxx 承諾 操作 結束 span aso Promise,簡單說就是一個容器,裏面保存著某個未來才會結束的事件(通常是一個異步操作)的結果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。 Promise 提供統一的 API

ES6 promise非同步載入的用法

1:單個promise     let p=new Promise(function (resolve, reject){       $.ajax({         url: 'data/arr2.tx

ES6 async await 試題

轉自:https://juejin.im/post/5c0397186fb9a049b5068e54 1、題目一 async function async1(){ console.log('async1 start') await async2() console.lo

Promise 試題

最近在複習 Promise 的知識,所以就做了一些題,這裡挑出幾道題,大家一起看看吧。 題目一 const promise = new Promise((resolve, reject) => {     console.log(1);     resolve();

ES6 promise的使用

一 對promise的理解   js中promise主要用於非同步執行的場景,兩個回撥函式resolve,reject。 二 用法   1.一般使用 1 function loadImg(url){ //定義一個函式 2 return new Promis

位數!試題

1 兩個陣列x[]、y[],長度為n,而且都經過排序從小到大排列,請編寫C/C++程式求出兩個數組合並後(仍然從小到大排列)的中位數,要求比較次數少於n。 #include "iostream.h" #include "stdio.h" #define num 4 in

Promise試題

push 代碼實現 aer 執行c 一輪 item 不可 失敗 新建 題目一 const promise = new Promise((resolve, reject) => { console.log(1); resolve(); cons

ES6Promise的用法

1、promise是一個建構函式,那就new一個出來 var p = new Promise(function(resolve,reject){    //做一些非同步操作    setTimeout(function(){        console.log("執行完成

一道promise試題引發的茅塞頓開

今天朋友參加面試,冒死發出面試題,有一道面試題是這樣的 題目:使用promise實現每隔一秒輸出一個1,一共三個 因為朋友在面試,緊張的心裡傳染到了我,使逼出了最強的自己,在還沒有用過promise的我竟然5分鐘寫出來了,而且是十天前根本沒看懂promise。程式碼如

原生Ajax寫法和ES6Promise結合Ajax寫法

原生AJAX var Ajax={ get: function(url, fn) { // XMLHttpRequest物件用於在後臺與伺服器交換資料 var xhr = new XMLHttpRequest(); xhr.open('G

阿里前端測試題--關於ES6Promise函式的理解與應用

今天做了阿里前端的筆試題目,原題目是這樣的 //實現mergePromise函式,把傳進去的陣列順序先後執行,//並且把返回的資料先後放到陣列data中const timeout = ms => new Promise((resolve, reject) => {setTimeout(() =

網路上總結python試題

【題目:001】| 說說你對zen of python的理解,你有什麼辦法看到它? Python之禪,Python秉承一種獨特的簡潔和可讀行高的語法,以及高度一致的程式設計模式,符合“大腦思維習慣”,使Python易於學習、理解和記憶。Python同時採用了一條

vue ES6promise的總結

總結:promise的then方法屬於微任務,而 setTimeout方法屬於巨集任務。在js事件執行機制中,先執行同步任務,再執行微任務,最後執行巨集任務。 在then方法中,若返回的是promise物件,不管是否包含巨集任務、微任務,都需要等待promise物件的狀態變為fulfilled時,才會呼

劍指Offer試題15(Java版):鏈表倒數第K個結點

head 計數器 easy sta 相同 ret white style 輸出 題目: 輸入一個鏈表。輸出該鏈表中倒數第k哥結點。 為了符合大多數人的習慣,本題從1開始計數。即鏈表的尾結點是倒數第1個結點。 比如一個鏈表有6個結點。從頭結點開始它們的值依次是1。2。

一些JAVA有趣的試題

你在 java培訓 計算機 擴展 調用 如果 科學計算 ble 運行   這幾天的JAVA培訓課上,不斷的喚醒自己對JAVA的理解(時間太長,大學學的快忘幹凈了),其中有一些很有趣的面試題,都是在摳JAVA細節(唉,我是那種見坑就往下跳的人,一做就錯),下面就來分享一下:

試題4:二維數組的查找

scrip turn all new cnblogs find auth ring 二維 在一個二維數組中,每一行都按照從左到右遞增的順序排序,每一列都按照從上到下遞增的順序排序。 請完成一個函數,輸入這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。 packag

Java基礎的一道試題

override ktr void ati star 打印 str @override ... 這個是我以前的一道面試題: public class MyThread extends Thread { @Override public void run() { tr