1. 程式人生 > >Promise面試題

Promise面試題

push 代碼實現 aer 執行c 一輪 item 不可 失敗 新建

題目一

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(‘success‘);也不會有作用。

答案:then:success1

題目三

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

解析

Promise.resolve方法的參數如果是一個原始值,或者是一個不具有then方法的對象,則Promise.resolve方法返回一個新的Promise對象,狀態為resolved,Promise.resolve方法的參數,會同時傳給回調函數。

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

答案 1

題目四

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

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

解析

紅燈3秒亮一次,綠燈1秒亮一次 ,黃燈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

例子中按照順序執行A,B但是輸出的結果卻是b,a。因為對於異步函數來說,並不會按順序執行完一個,再執行後一個。

這道題主要考察用Promise控制異步流程,讓這些涵涵素,一個執行完,再執行下一個。

答案

// 保存數組中的函數執行後的結果
var data = [];

// Promise.resolve方法調用時不帶參數,直接返回一個resolved狀態的 Promise 對象。
var sequence = Promise.resolve();

ajaxArray.forEach(function (item) {
    // 第一次的 then 方法用來執行數組中的每個函數,
    // 第二次的 then 方法接受數組中的函數執行後返回的結果,
    // 並把結果添加到 data 中,然後把 data 返回。
    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);

解析

這道題主要理解js執行機制

第一輪事件循環

先執行宏任務,主script,new Promise立即執行,輸出 3,

執行p這個new Promise操作,輸出 7,

發現setTimeout,將回調函數放入下一輪任務隊列(Event Quene),p的then,暫且命名為then1,放入微任務隊列,且first也有then,命名為then2,放入微任務隊列。執行console.log(4),輸出 4,宏任務執行結束。

再執行微任務,執行then1,輸出 1,

執行then2,輸出 3.

第一輪事件循環結束,開始執行第二輪。

第二輪事件循環

先執行宏任務裏面的,也就是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 loading,輸入一個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面試題