ES 6のPromiseの面接問題について

10138 ワード

説明
最近Promiseの知識を復習していますので、いくつかの問題を解いてみました.
テーマ1
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
タイトル2
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('success2');も役に立たない.
答え
then: success1
タイトル3
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
テーマ4
赤信号は三秒に一回、青信号は一秒に一回、黄灯は二秒に一回点灯します.どのように3つのライトを交互に点灯させますか?(Promseで実現)3つの点灯関数は既に存在します.
function red() {
    console.log('red');
}
function green() {
    console.log('green');
}
function yellow() {
    console.log('yellow');
}
解析
赤信号は三秒に一回、青信号は一秒に一回、黄灯は二秒に一回、つまり三秒で一回のred関数を実行して、二秒に一回のグリーン関数を実行して、一秒に一回のyellow関数を実行して、絶えず交互に明かりを点灯します.
答え
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();
タイトル5
mergPromise関数を実現して、転送された配列を順番に順を追って実行して、しかも帰ってくるデータを順番に配列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は、これらの非同期関数については、順番に1つを実行しきれず、後の1つを実行します.この問題はPromiseを使って非同期の流れを制御することを試験して、私達は方法を考えて、これらの関数を譲って、1つの実行が終わった後に、更に次を実行して、解答を見ましょう.
答え
//               
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;
タイトル6
以下のコードは最後に何を出力しますか?
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執行メカニズムがよく分からない友達にこの文章を紹介してください.
今回は、JavaScriptの実行メカニズムを徹底的に理解します.
イベントサイクル
先にマクロタスクを実行し、メインscript、new Promiseは直ちに実行し、【3】を出力し、pというnew Promise操作を実行し、【7】を出力し、setTimeoutを発見したら、次のタスクキュー(Event Que)に戻ります.console.log(4)を実行し、【4】を出力し、マクロタスクの実行が終了します.また、マイクロタスクを実行し、then 1を実行し、【1】を出力し、then 2を実行し、【2】を出力します.これで第1ラウンドは終了します.第二ラウンドの実行を開始します.
第二ラウンドイベント循環
まずマクロタスクの中の、つまりsetTimeoutのコールバックを実行して【5】を出力します.resolve(6)は有効ではありません.pというPromiseの状態が一旦変わると、変化しないからです.
答え
3
7
4
1
2
5 
タイトル7
8つのピクチャリソースがあるurlは、配列urlsに格納されている.(urls = ['http://example.com/1.jpg', ...., 'http://example.com/8.jpg'])です.もう一つの関数function loadImgがあります.urlリンクを入力して、Promiseに戻ります.このPromiseは写真のダウンロードが完了した時にreolveをダウンロードしました.失敗したら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枚のピクチャーを同時に請求して、1枚のピクチャーがロードし終わった後に、また引き続き1枚のピクチャーの要求を開始して、併発数を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に対する理解はいいと思います.
最後に、もし文章の中に足りないところや間違ったところがあったら、友達に指摘してもらって、ありがとうございます.文章の内容が足りないと感じたら、最後にテーマに関する文章があります.見てもいいです.
参照
ECMAScript 6入門——阮一峰
ES 6シリーズの私達はPromiseについて話します.
Promise応用に関する面接問題
アリの先端テスト問題--ES 6のPromise関数の理解と応用について
今回は、JavaScriptの実行メカニズムを徹底的に理解します.
つのPromiseの面接試験問題
ES 6原生Promiseのあらゆる方法紹介(添付の応用シーンテーマ)
Promise非同期プロセス制御