ES6-promise

異步編程簡述:

  • 不管是在瀏覽器環境中仍是在node環境中,咱們都會使用JavaScript
  • 完成各類異步操做,如在瀏覽器環境中的定時器、事件、ajax等或是node環
  • 境中的文件讀取、事件等。伴隨着異步編程的就是回調機制(複習jQuery)。
  • 明確一點異步編程避免不了回調。
let obj = {};

let fs = require('fs');

fs.readFile('./data/name.txt', 'utf-8', (err, data) => {
    obj.name = data;
    Store.fire(obj);
});
fs.readFile('./data/number.txt', 'utf-8', (err, data) => {
    obj.number = data;
    Store.fire(obj);
});
fs.readFile('./data/score.txt', 'utf-8', (err, data) => {
    obj.score = data;
    Store.fire(obj);
});


let Store = {
    list: [],
    times: 3,
    subscribe (func) {
        this.list.push(func);
    },
    fire (...args) {
        --this.times == 0 && this.list.forEach(ele=>{
            ele.apply(null, args);
        })
    }
}
Store.subscribe(show);

function show (data) {
    console.log(data);
}
複製代碼

異步編程問題:

  • 產生回調地獄,難於維護和擴展。
  • try catch只能捕獲同步代碼中出現的異常。
  • 同步併發的異步存在必定的問題。

解決方案:

  • ES6 Promise能夠解決回調地獄、以及同步併發的異步問題。(異步操做
  • 的異常捕獲有其餘方式解決。)
  • 但依舊會有明顯的回調痕跡,以後學習ES6的generator 、ES7的
  • async await爭取讓異步代碼看起來和同步同樣。寫起來更優雅一些。

Promise(.then)

微任務和宏任務

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');
複製代碼

打印順序是什麼?javascript

正確答案是: ==script start, script end, promise1, promise2, setTimeout==java

  • 每一個線程都會有它本身的event loop(事件循環),因此都能獨立運行。然而全部同源窗口會共享一個event loop以同步通訊。event loop會一直運行,來執行進入隊列的宏任務。一個event loop有多種的宏任務源(譯者注:event等等),這些宏任務源保證了在本任務源內的順序。可是瀏覽器每次都會選擇一個源中的一個宏任務去執行。這保證了瀏覽器給與一些宏任務(如用戶輸入)以更高的優先級。好的,跟着我繼續……
宏任務(task)
  • 瀏覽器爲了可以使得JS內部task與DOM任務可以有序的執行,會在一個task執行結束後,在下一個 task 執行開始前,對頁面進行從新渲染 (task->渲染->task->...)
  • 鼠標點擊會觸發一個事件回調,須要執行一個宏任務,而後解析HTMl。還有下面這個例子,setTimeout
  • setTimeout的做用是等待給定的時間後爲它的回調產生一個新的宏任務。這就是爲何打印‘setTimeout’在‘script end’以後。由於打印‘script end’是第一個宏任務裏面的事情,而‘setTimeout’是另外一個獨立的任務裏面打印的。
微任務(Microtasks )
  • 微任務一般來講就是須要在當前 task 執行結束後當即執行的任務,好比對一系列動做作出反饋,或或者是須要異步的執行任務而又不須要分配一個新的 task,這樣即可以減少一點性能的開銷。只要執行棧中沒有其餘的js代碼正在執行且每一個宏任務執行完,微任務隊列會當即執行。若是在微任務執行期間微任務隊列加入了新的微任務,會將新的微任務加入隊列尾部,以後也會被執行。微任務包括了mutation observe的回調還有接下來的例子promise的回調。node

  • 一旦一個pormise有告終果,或者早已有告終果(有告終果是指這個promise到了fulfilled或rejected狀態),他就會爲它的回調產生一個微任務,這就保證了回調異步的執行即便這個promise早已有告終果。因此對一個已經有告終果的promise調用.then(yey, nay)會當即產生一個微任務。這就是爲何‘promise1’,'promise2'會打印在‘script end’以後,由於全部微任務執行的時候,當前執行棧的代碼必須已經執行完畢。‘promise1’,'promise2'會打印在‘setTimeout’以前是由於==全部微任務總會在下一個宏任務以前所有執行完畢。==ajax

.then的鏈式操做編程

  1. 上一個不拋出錯誤的話, 那下一個then會默認執行成功回調函數, 拋出錯誤的話會執行失敗回調函數
  2. 上一個then的返回至會做爲下一個then註冊函數的執行參數
  3. 上一個then返回一個Promise對象的話,後面的then就會成爲這個對象的註冊函數
// resolve 和 resolve是互斥事件
let oP = new Promise((resolve, reject)=> {
    // 在構造函數中同步執行
    console.log('123');
    reject('aaa');
    resolve('ddd');
    
});

oP.then((data)=> { // 被看成微任務
    // 異步執行
    // setTimeout 是宏任務
    // 宏任務          微任務
    // task queue 1    task queue 2
    // 微任務有優先執行權
    console.log(data);
    return 20;
}, (reason)=>{
    console.log(reason);
    return 30;
}).then((data)=> {
    console.log('ok then2 ' + data);
}, (reason)=> {
    console.log('no then2 ' + reason);
});

// .then的鏈式操做
// 上一個不拋出錯誤的話, 那下一個then會默認執行成功回調函數, 拋出錯誤的話會執行失敗回調函數
// 上一個then的返回至會做爲下一個then註冊函數的執行參數
// 上一個then返回一個Promise對象的話,後面的then就會成爲這個對象的註冊函數 
複製代碼

用解決回調地獄

回調地獄數組

// name.txt: ./data/number.txt
// number.txt: ./data/score.txt
// score.txt: 100
let fs = require('fs');
fs.readFile('./data/name.txt', 'utf-8', (err, data)=> {
    console.log(data);
    if(data) {
        fs.readFile(data, 'utf-8', (err, data)=> {
            console.log(data);
            if(data) {
                fs.readFile(data, 'utf-8', (err, data)=> {
                    console.log(data);
                });
            }
        });
    }
});
複製代碼

使用Promise後promise

// name.txt: ./data/number.txt
// number.txt: ./data/score.txt
// score.txt: 100
let fs = require('fs');
function readFile(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, 'utf-8', (err, data) => {
            if (data) {
                resolve(data);
            }
        });
    });
}
readFile('./data/name.txt').then((data) => {
    console.log(data);
    return readFile(data);
}).then((data) => {
    console.log(data);
    return readFile(data);
}).then((data) => {
    console.log(data);
});
// node運行結果 
// ./data/number.txt
// ./data/score.txt
// 100
複製代碼

.then .catch .finally

// 鏈式操做當遇到空的.then時,就當它不存在
let oP = new Promise((res, rej) => {
    res();
});
oP.then(() => {
    throw new Error('www');
}).then(() => {

}).catch((err)=>{
    console.log(err);
    // throw new Error('dasf');
    return 10;
}).then((val)=>{
    console.log('catch after ok:' + val); // 輸出
},(reason)=>{
    console.log('catch after no:' + reason) // 解開catch報錯註釋的時候輸出
}).finally(()=>{
    console.log('over'); 
});
複製代碼

Promise.all()獲取併發進程的結果 Promise.race() 賽跑

  • Promise.all能夠將多個Promise對象實例包裝成一個新的Promise實例瀏覽器

  • 同時,成功和失敗的返回至使不一樣的,成功的時候返回的是一個結果數組失敗的時候則返回最早被reject失敗狀態的值bash

// 在數組中的全部異步進程都成功執行後再回執行.then後的resolve 有一個進程不成功都只會執行reject
Promise.all([readFile('./data/number.txt'), readFile('./data/name.txt'), readFile('./data/score.txt')]).then((val) => {
    console.log(val);
}, (reason)=> {
    console.log('no');
})
複製代碼
  • 顧名思義, Promise.race就是賽跑的意思,意思就是說,Promise.race([p1,p2,p3])裏面那個結果得到的快,就返回那個結果,無論結果自己世成功狀態仍是失敗狀態
// race 賽跑  數組中的線程誰先成功 後面的then參數就是誰的返回值
Promise.race([readFile('./data/number.txt'), readFile('./data/name.txt'), readFile('./data/score.txt')]).then((val) => {
    console.log(val);
}, (reason)=> {
    console.log('no');
})
複製代碼

MyPromise

執行併發

let oP = new MyPromise((res, rej) => {
                    res('ee');
            });
            oP.then((val)=> {
                console.log(val,'ok')
                return new MyPromise((res, rej)=>{
                    res(0);
                });
            },(reason)=>{
                console.log(reason,'no');
                return 12;
            }).then(val=> {
                console.log(val);
            });
複製代碼

MyPromise文件

function MyPromise(executor) {
    var self = this;
    self.status = 'pending'; // 狀態
    self.resolveValue = null;
    self.rejectReason = null;
    self.resolveCallBacksList = []; // 實如今執行函數中可使用異步例如setTimeout
    self.rejectCallBacksList = []; // 
    function resolve(value) {
        if (self.status === 'pending') {
            self.status = 'Fulfilled';
            self.resolveValue = value;
            self.resolveCallBacksList.forEach((ele) => {
                ele();
            });
        }
    }
    function reject(reason) {
        if (self.status === 'pending') {
            self.status = 'Rejected';
            self.rejectReason = reason;
            self.rejectCallBacksList.forEach((ele) => {
                ele();
            });
        }
    }
    // 報錯時的處理方法 全部執行用 try catch 包裹
    try {
        // 執行咱們在外界傳入的函數, 將對象裏的resolve, reject函數傳進去, 在new一個新的Promise時, 本身定義的名字只是用來接收 有點繞?
        executor(resolve, reject); 
    } catch (e) {
        reject(e);
    }
};


// 若是then的返回值是對象時的處理方式
function ResolutionReturnPromise(nextPromise, returnValue, res, rej) {
    if(returnValue instanceof MyPromise) {
        // Promise對象
        returnValue.then(function (val) {
            res(val);
        }, function (reason) {
            rej(reason);
        })
    }else {
        res(returnValue);
    }
}
MyPromise.prototype.then = function (onFulfilled, onRejected) {
    var self = this;
    // 判斷註冊函數是否爲空, 若是是true就返回如下函數 將返回值做爲下一個then的參數
    if (!onFulfilled) {
        onFulfilled = function (val) {
            return val;
        }
    }
    if (!onRejected) {
        onRejected = function (reason) {
            return new Error(reason);
        }
    }
    // 鏈式操做? new一個新的Promise時,裏面的代碼是同步執行的
    var nextPromise = new MyPromise(function (res, rej) {
        if (self.status === 'Fulfilled') { // 判斷狀態 下同
            setTimeout(() => {
                try {
                    var nextResolveValue = onFulfilled(self.resolveValue); // 執行註冊函數並接收返回值
                    ResolutionReturnPromise(nextPromise, nextResolveValue, res, rej);
                } catch (e) {
                    rej(e);
                }
            }, 0);

        }
        if (self.status === 'Rejected') {
            setTimeout(() => {
                try {
                    var nextRejectReason = onRejected(self.rejectReason);
                    ResolutionReturnPromise(nextPromise, nextRejectReason, res, rej);
                } catch (e) {
                    rej(e);
                }
            }, 0);

        }
        if (self.status === 'pending') {
            if (onFulfilled) {
                self.resolveCallBacksList.push(function () {
                    setTimeout(() => {
                        try {
                            var nextResolveValue = onFulfilled(self.resolveValue);
                            ResolutionReturnPromise(nextPromise, nextResolveValue, res, rej);
                        } catch (e) {
                            rej(e);
                        }
                    }, 0);
                });
            }
            if (onRejected) {
                self.rejectCallBacksList.push(function () {
                    setTimeout(() => {
                        try {
                            var nextRejectReason = onRejected(self.rejectReason);
                            ResolutionReturnPromise(nextPromise, nextRejectReason, res, rej);
                        } catch (e) {
                            rej(e);
                        }
                    }, 0);
                });
            }
        }
    });

    return nextPromise; // 返回一個Promise對象
}

MyPromise.all = function (promiseArr) {
    return new MyPromise(function (resolve, reject) {
        if(!Array.isArray(promiseArr)) {
            return reject(new TypeError("argument must be anarray"));
        }
        var promiseArrValue = [];
        promiseArr.forEach((promise, index) => {
            promise.then((val)=>{
                promiseArrValue.push(val);
                if(index == promiseArr.length - 1) {
                    return resolve(promiseArrValue);
                }
            }, (reason)=>{
                reject(reason);
            })
        });
    });
}


複製代碼
相關文章
相關標籤/搜索