詳解promise

我家以前住在巷子裡發表於2018-03-16

promise是什麼?

JavaScript 和 node 都是單執行緒非阻塞的執行方式,所以會出現一個非同步回撥的問題。promise就是為了解決這類問題 前端常見的非同步操作有:

setTimeout setInterval ajax

現在基本都使用async和await

非同步發展

callback

最初解決非同步方式是通過callback回撥去解決

例1:

let callback = () => {
    console.log('callback1');
}
setTimeout(callback, 1000)
console.log('begin');

// 列印順序  
// begin 
// callback1
複製程式碼

例1是最典型的通過callback去解決非同步問題, 但是有兩個問題

1.無止境的回撥,不利於程式碼維護如下,在ajax請求資料時尤其常見

2.好幾個請求,很難同時得到資料(可以通過程式碼做到,比較煩)

promise + Q

為了解決上面兩個問題出現了promise,用法如下

// Promise 是一個類通過new得到它的例項
// new Promise(executor) 傳入的函式就是executor
let promise = new Promise((resolve, reject) => {
    // 傳入一個方法,這個方法裡面放的就是需要執行的非同步程式 
    //這個方法有兩個引數 resolve表示成功 reject表示失敗 都是函式
    // 如果呼叫resolve會走then的第一個方法,即成功方法
    // 如果呼叫reject會走then的第二個方法,即失敗方法 ,如果有報錯也會走失敗方法
    setTimeout(() => {
        console.log('2')
        resolve('resolveData')
        // reject('err')
    })
    console.log('1')
});
// 在Promise的原型上有一個then方法,會在primiose的executor執行後根據不同情況執行

promise.then((data) => {
    console.log(data)
    return data
}, (err) => {})

// 列印順序 1 => 2 => 'resolveData'

// then方法在Promise內部是返回一個新的Promise例項,所以可以實現鏈式呼叫,並且將return出去的的值作為下一個then相應方法的引數

promise.then((data) => {
    console.log(data + '2')
    return data
}, (err) => {}).then((data) => {
    console.log(data)
}, () => {}) // 'resolveData' => 'resolveData2'

// 如果第一個then進入reject方法,但是在裡面並沒有報錯,那麼還是會進入下一個resolve方法,同樣return出去的值也是第二個then相應方法的引數

複製程式碼

promise 常用方法

//catch 集體捕獲錯誤
promise.then(() => {}).then(() => {}).catch(() => {})
// 不需要再每一個then裡面都加上失敗函式,可以選擇在最後加上catch,如果失敗或報錯直接走到這

// Promise.all
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve({a: 1});
    })
})
let promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve({b: 2});
    })
})
let promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve({c: 2});
    })
})

Promise.all([promise, promise1, promise2]).then((data) => {
    console.log('data', data); // data [ { a: 1 }, { b: 2 }, { c: 2 } ]
}, (err) => {
    console.log('err', err);
})
// 多個promise執行resolve後再走then第一個方法,有一個報錯或執行reject直接走then第二個方法

// Promise.race 只要有一個promise成功了 就算成功。如果第一個失敗了就失敗了

Promise.race([promise, promise1, promise2]).then((data) => {
    console.log('data', data); // data { a: 1 }
}, (err) => {
    console.log('err', err);
})

// Promise.resolve  Promise.reject 分別返回一個成功 失敗的promise
複製程式碼

Q庫是一個promise的庫,裡面有all defer等方法,最後被node收購了,現在的promise也有all方法但是沒有defer,很簡單後面我們可以手寫一個

generator + co

generator 函式需要用*來做表示,函式內部配合yield去暫停並且返回一個物件 { value: 1, done: false }

value的值是yield後面跟著的數值,

done的值是一個布林型別,表示函式有沒有執行完畢,false表示還沒有執行完,可以繼續通過it.next()呼叫;

function *mygenerator() {
    let m = yield 1
    console.log(m, '1'); // 22 '1'
    
    let n = yield 2
    console.log(n, '2'); // 33 '2'
}

let it = mygenerator(); // generator返回一個it值裡面有一個next方法可以呼叫
console.log(it.next(11)); // { value: 1, done: false }
console.log(it.next(22)); // { value: 2, done: false }
console.log(it.next(33)); // { value: undefined, done: true }
複製程式碼

co庫 npm install co 可以自動的將generator進行迭代

let co = require('co');
let co = require('co');

function *mygenerator() {
    let m = yield (() => { return {a: 1}})()
    console.log(m, '1'); // { a: 1 } '1'
    
    let n = yield (() => {return {b: 2}})()
    console.log(n, '2'); // { b: 2 } '2'

    return {m ,n}
}

co(mygenerator()).then((data) => {
    console.log('data', data) // data { m: { a: 1 }, n: { b: 2 } }
})

// 在使用co庫時yield後面只能跟 function, promise, generator, array, or object 否則會報錯
複製程式碼

async + await

async + await 就是 generator和co的語法糖 用法簡單

async function r() {
    try{
        let m = await (() => { return {a: 1}})();
        let n = await (() => { return {a: 2}})();
        return {m, n};
    }catch(e){ // 如果出錯會catch
        console.log('err',e)
    }
}

r().then((data) => {
    console.log('data', data);
})
複製程式碼

根據promiseA+實現一個自己promise

function Promise(executor) { // executor是一個執行函式
    let self = this;
    self.status = 'pending';
    self.value = undefined; // 預設成功的值
    self.reason = undefined; // 預設失敗的原因
    self.onResolvedCallbacks = []; // 存放then成功的回撥
    self.onRejectedCallbacks = []; // 存放then失敗的回撥
    function resolve(value) { // 成功狀態
        if (self.status === 'pending') {
            self.status = 'resolved';
            self.value = value;
            self.onResolvedCallbacks.forEach(function (fn) {
                fn();
            });
        }
    }
    function reject(reason) { // 失敗狀態
        if (self.status === 'pending') {
            self.status = 'rejected';
            self.reason = reason;
            self.onRejectedCallbacks.forEach(function (fn) {
                fn();
            })
        }
    }
    try {
        executor(resolve, reject)
    } catch (e) { // 捕獲的時候發生異常,就直接失敗了
        reject(e);
    }
}
function resolvePromise(promise2, x, resolve, reject) {
    // 有可能這裡返回的x是別人的promise
    // 儘可能允許其他亂寫
    if (promise2 === x) { //這裡應該報一個型別錯誤,有問題
        return reject(new TypeError('迴圈引用了'))
    }
    // 看x是不是一個promise,promise應該是一個物件
    let called; // 表示是否呼叫過成功或者失敗
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        // 可能是promise {},看這個物件中是否有then方法,如果有then我就認為他是promise了
        try { // {then:1}
            let then = x.then;
            if (typeof then === 'function') {
                // 成功
                then.call(x, function (y) {
                    if (called) return
                    called = true
                    // y可能還是一個promise,在去解析直到返回的是一個普通值
                    resolvePromise(promise2, y, resolve, reject)
                }, function (err) { //失敗
                    if (called) return
                    called = true
                    reject(err);
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (called) return
            called = true;
            reject(e);
        }
    } else { // 說明是一個普通值1
        resolve(x); // 表示成功了
    }
}
Promise.prototype.then = function (onFulfilled, onRjected) {
    //成功和失敗預設不穿給一個函式
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    }
    onRjected = typeof onRjected === 'function' ? onRjected : function (err) {
        throw err;
    }
    let self = this;
    let promise2; //返回的promise
    if (self.status === 'resolved') {
        promise2 = new Promise(function (resolve, reject) {
            // 當成功或者失敗執行時有異常那麼返回的promise應該處於失敗狀態
            // x可能是一個promise 也有可能是一個普通的值
            setTimeout(function () {
                try {
                    let x = onFulfilled(self.value);
                    // x可能是別人promise,寫一個方法統一處理
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })
        })
    }
    if (self.status === 'rejected') {
        promise2 = new Promise(function (resolve, reject) {
            setTimeout(function () {
                try {
                    let x = onRjected(self.reason);
                    resolvePromise(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })

        })
    }
    // 當呼叫then時可能沒成功 也沒失敗
    if (self.status === 'pending') {
        promise2 = new Promise(function (resolve, reject) {
            // 此時沒有resolve 也沒有reject
            self.onResolvedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onFulfilled(self.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                })
            });
            self.onRejectedCallbacks.push(function () {
                setTimeout(function () {
                    try {
                        let x = onRjected(self.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                })
            });
        })
    }
    return promise2;
}
// 捕獲錯誤的方法
Promise.prototype.catch = function (callback) {
    return this.then(null, callback)
}
// 解析全部方法
// let arr = [];
// arr[1] = 100;
// console.log(arr.length)
Promise.all = function (promises) {
    //promises是一個promise的陣列
    return new Promise(function (resolve, reject) {
        let arr = []; //arr是最終返回值的結果
        let i = 0; // 表示成功了多少次
        function processData(index, y) {
            arr[index] = y;
            if (++i === promises.length) {
                resolve(arr);
            }
        }
        for (let i = 0; i < promises.length; i++) {
            promises[i].then(function (y) {
                processData(i, y)
            }, reject)
        }
    })
}
// 只要有一個promise成功了 就算成功。如果第一個失敗了就失敗了
Promise.race = function (promises) {
    return new Promise(function (resolve, reject) {
        for (var i = 0; i < promises.length; i++) {
            promises[i].then(resolve,reject)
        }
    })
}
// 生成一個成功的promise
Promise.resolve = function(value){
    return new Promise(function(resolve,reject){
        resolve(value);
    })
}
// 生成一個失敗的promise
Promise.reject = function(reason){
    return new Promise(function(resolve,reject){
        reject(reason);
    })
}
Promise.defer = Promise.deferred = function () {
    let dfd = {};
    dfd.promise = new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd
}
// mjs
module.exports = Promise;
複製程式碼

有問題歡迎指正

相關文章