認識JavaScript Promise

參考:javascript

EC前端 - Promise - http://www.ecmaer.com/javascript/nativeObj/promise.htmlhtml

關於Promise:前端

  • 什麼是 Promise?
    Promise 是異步編程的一種解決方案,比傳統的異步解決方案【回調函數】和【事件】更合理、更強大。現已被 ES6 歸入進規範中。
    Promise是一個方案,用來解決多層回調嵌套的解決方案

Promise 的常規寫法:java

new Promise(請求1)
    .then(請求2(請求結果1))
    .then(請求3(請求結果2))
    .then(請求4(請求結果3))
    .then(請求5(請求結果4))
    .catch(處理異常(異常信息))

Promise 的寫法更爲直觀,而且可以在外層捕獲異步函數的異常信息jquery

  • Promise 解決的痛點是什麼?
    回調地獄
    須要根據前一個網絡請求的結果,再去執行下一個網絡請求,代碼大概以下:
請求1(function(請求結果1){
    請求2(function(請求結果2){
        請求3(function(請求結果3){
            請求4(function(請求結果4){
                請求5(function(請求結果5){
                    請求6(function(請求結果3){
                        ...
                    })
                })
            })
        })
    })
})

回調地獄帶來的負面做用有如下幾點:
一、代碼臃腫;
二、可讀性差;
三、耦合度太高,可維護性差;
四、代碼複用性差;
五、容易滋生 bug;
六、只能在回調裏處理異常;
爲了能使用一種更加友好的代碼組織方式,解決異步嵌套的問題:es6

let 請求結果1 = 請求1();
let 請求結果2 = 請求2(請求結果1);
let 請求結果3 = 請求3(請求結果2);
let 請求結果4 = 請求2(請求結果3);
let 請求結果5 = 請求3(請求結果4);
  • Promise 的業界實現都有哪些?
    業界著名的 Q 和 bluebird,bluebird 甚至號稱運行最快的類庫面試

  • Promise 解決的痛點還有其餘方法能夠解決嗎?若是有,請列舉。
    爲了解決「回調地獄」,咱們可使用文中所述的這五種經常使用方法:
    一、function拆解
    回調嵌套所帶來的一個重要問題就是代碼不易閱讀與維護。由於廣泛來講,過多的縮進(嵌套)會極大的影響代碼的可讀性。基於這一點,
    能夠進行一個最簡單的優化——將各步拆解爲單個的function
    二、事件發佈/訂閱模式
    咱們能夠監聽某一事件,當事件發生時,進行相應回調操做;另外一方面,當某些操做完成後,經過發佈事件觸發回調。這樣就能夠將本來捆綁在一塊兒的代碼解耦。
    三、Promise
    四、Generator
    generator是es6中的一個新的語法。在function關鍵字後添加*便可將函數變爲generator
const gen = function* () {
    yield 1;
    yield 2;
    return 3;
}

let g = gen();
g.next(); // { value: 1, done: false }
g.next(); // { value: 2, done: false }
g.next(); // { value: 3, done: true }
g.next(); // { value: undefined, done: true }

generator函數有一個最大的特色,能夠在內部執行的過程當中交出程序的控制權,yield至關於起到了一個暫停的做用;而當必定狀況下,外部又將控制權再移交回來
五、async / await
es7中的async/await
在async函數中可使用await語句。await後通常是一個Promise對象ajax

async function foo () {
    console.log('開始');
    let res = await post(data);
    console.log(`post已完成,結果爲:${res}`);
};

四、Promise 如何使用?編程

// 聲明函數
function run(callback) {
    let parmas = 0;
    if (callback) callback(parmas);
};
function fnStep1(callback) {
    let parmas = 123;
    if (callback) callback(parmas);
};
function fnStep2(callback) {
    let parmas = 456;
    if (callback) callback(parmas);
};
function fnStep3(callback) {
    let parmas = 789;
    if (callback) callback(parmas);
};
// fnStep4 ...

// 傳統使用回調的寫法
run(function (parmas) {
    // parmas = 0
    console.log(parmas);
    fnStep1(function (parmas1) {
        // parmas = 123
        console.log(parmas1);
        fnStep2(function (parmas2) {
            // parmas = 456
            console.log(parmas2);
            fnStep3(function (parmas3) {
                // ...
                // 一直嵌套
            });
        });
    });
});


let p = new Promise((resolve, reject) => {
    const parmas = 0;
    resolve(parmas); // fulfilled
    //   reject("failure reason"); // rejected
})

p.then(
    (parmas) => {
        // parmas,resolve返回的值
        console.log(parmas);  //0
        return 123; //返回值給下一個then
    }
    )
    .then(
    (parmas) => {
        // parmas,上一個then返回的值
        console.log(parmas); //123
        return 456; //返回值給下一個then
    }
    )
    .then(
    (parmas) => {
        // parmas,上一個then返回的值
        console.log(parmas);  //456
        return 789; //返回值給下一個then
    })
  • Promise 經常使用的方法,方法的做用?
    一、race
    多個promise 任務同時執行,只返回最早執行完的 Promise 任務的結果;
    二、all
    多個promise 任務同時執行,返回全部promise 任務的執行結果;

六、Promise 在事件循環中的執行過程是怎樣的?promise

  • 構造函數傳入的參數是什麼類型?
    它接收的參數是一個匿名函數,任何狀況下,它裏面的js最早執行。
    這個匿名函數也有二個參數:
    一、resolve,完成,操做成功時調用。
    二、reject,失敗,操做失敗時調用。

  • 傳入的該函數是會馬上執行的嗎?
    會在本次new 操做馬上執行。
    第一次resolve就肯定了本身是成功仍是失敗。第二次沒用了。添加reject也改變不了
  • 若調用了兩次resolve方法會怎麼樣?
    只執行第一次resolve

  • 發生異常會怎麼樣?
    promise的原理?jquery的ajax返回的是promise對象嗎?(百度面試)
    promise 只有2個狀態,成功和失敗,怎麼讓一個函數不管成功和失敗都能被調用?
    Promise.all() 是幹什麼用的,怎麼用?

  • 初始化Promise對象的方法
    new Promise(fn)
    Promise.resolve(fn)

/*
*end
*nextTick
*then
*setImmediate
*
*process.nextTick 和 promise.then 都屬於 microtask
* 而 setImmediate 屬於 macrotask
* 在事件循環的 check 階段執行
* 事件循環的每一個階段(macrotask)之間都會執行 microtask,事件循環的開始會先執行一次 microtask
*
 */

process.nextTick(()=>{
    console.log('nextTick');
});
Promise.resolve().then(()=>{
    console.log('then');
});

setImmediate(()=>{
    console.log('setImmediate');
});
console.log('end');

Promise擴展之事件循環機制:
macrotasks:

  • setTimeout
  • setInterval
  • setImmediate
  • requestAnimationFrame
  • I/O
  • UI rendering

microtasks:

  • process.nextTick
  • Promises
  • Object.observe
  • MutationObserver

任務能夠分紅兩種,一種是同步任務(synchronous),另外一種是異步任務(asynchronous)。
同步任務指的是,在主線程上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;
異步任務指的是,不進入主線程、而進入"任務隊列"(task queue)的任務,只有"任務隊列"通知主線程,某個異步任務能夠執行了,該任務纔會進入主線程執行。

執行優先級的問題 microtasks > macrotasks

在每一次事件循環中,macrotask 只會提取一個執行,而 microtask 會一直提取,直到 microtasks 隊列清空。
而事件循環每次只會入棧一個 macrotask ,主線程執行完該任務後又會先檢查 microtasks 隊列並完成裏面的全部任務後再執行 macrotask

setImmediate(function(){
    console.log(1);
},0);
setTimeout(function(){
    console.log(2);
},0);
new Promise(function(resolve){
    console.log(3);
    resolve();
    console.log(4);
}).then(function(){
    console.log(5);
});
console.log(6);
process.nextTick(function(){
    console.log(7);
});
console.log(8);

//3 4 6 8 7 5 2 1
相關文章
相關標籤/搜索