Javascript Promise学习过程总结

Callback函数

JS早期实现异步都是采用回调方式callback来实现异步任务的。包括事件回调,setTimeout,setInterval, Ajax等,采起这样的方式写代码,功能实现没有问题,可是不够优雅清晰,容易出现回调地狱。jquery

Promise

Promise A plus 中文规范 英文规范git

实现过程(一)

同步基础框架

new Promise(function(resovle, reject){
    
})
复制代码

从上代码能够看出构造函数接受一个函数。github

const PENDING = "pending"; //等待
const FULFILLED = "fulfilled"; //已完成
const REJECTED = "rejected"; // 已拒绝

function Promise(executor) {
    let self = this;
    self.status = PENDING;

    self.value;
    self.reason;


    function resolve(value) {
        if (self.status === PENDING) {
            self.status = FULFILLED;
            self.value = value;
        }
    }

    function reject(reason) {
        if (self.status === PENDING) {
            self.status = REJECTED;
            self.reason = reason;
        }
    }
    try { // 规范提到,执行器抛异常会reject
        executor(resolve, reject);
    } catch(e) {
        reject(e)
    }
}
复制代码

then方法实现

Promise.prototype.then = function (onFulfilled, onRjected) {
    let self = this;
    /** * onFulfilled 和 onRejected 都是可选参数。 * 若是 onFulfilled 不是函数,其必须被忽略 * 若是 onRejected 不是函数,其必须被忽略 */
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value) {
        return value;
    };
    onRjected = typeof onRjected === 'function' ? onRjected : function(reason) {
        throw reason;
    }
    
    if (self.status === FULFILLED) {
        onFulfilled(self.value);
    }
    if (self.status === REJECTED) {
        onRjected(self.reason);
    }
}
复制代码

实现过程(二)

异步调用实现

promise实例能够屡次then,当成功后会将then中的成功方法按顺序执行,能够先将then中的成功的回调和失败的回调存到数组内,当成功时调用成功或失败的数组。npm

// 添加then的回调队列
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
复制代码
// 成功时候调用队列里的handler
self.onResolvedCallbacks.forEach(function (fn, index, array) {
    fn()
});
复制代码
// 失败时候调用队列里的handler
self.onRejectedCallbacks.forEach(function (fn, index, array) {
    fn()
})
复制代码

实现过程(三)

当链式调用then()时候,jquery能实现链式调用靠的就是返回this,可是promise不能返回this,promise实现链式调用靠的是返回一个新的promise。 并且then()方法能够什么都不穿,实现穿透调用下一个then()...数组

Promise.prototype.then = function (onFulfilled, onRjected) {
    let self = this;

    let promise2 = null; //返回的promise

    // 咱们的代码能够在then中什么都不传 promise中值的穿透
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    };
    
    onRjected = typeof onRjected === 'function' ? onRjected : function (reason) {
        throw reason;
    }

    if (self.status === FULFILLED) {
        promise2 = new Promise(function (onFulfilled2, onRjected2) {
            let result = onFulfilled(self.value);
            onFulfilled2(result);
        });
    }
    
    if (self.status === REJECTED) {
        promise2 = new Promise(function (onFulfilled2, onRjected2) {
            let reason = onRjected(self.reason);
            onRjected2(reason);
        });

    }

    if (self.status === PENDING) {
        promise2 = new Promise(function (resolve, reject) {
            self.onResolvedCallbacks.push(function () {
                let value = onFulfilled(self.value);
                resolve(value);
            });

            self.onRejectedCallbacks.push(function () {
                let reason = onRjected(self.reason);
                reject(reason);
            })
        });
    }
    return promise2;
}
复制代码

实现过程(四)

若是then中不管是成功的回调仍是失败的回调,只要返回告终果(return xxx)就会走下一个then中的成功,若是有错误(throw new Error)就走下一个then的失败。promise

若是第一个promise返回一个普通值,会进到下一次then的成功的回调,若是第一个promise返回了一个promise,须要等待返回的promise执行后的结果传递给下一次then中。并发

function resolvePromise(promise2, x, resolve, reject) {
    // 有可能这里返回的x是别人的promise
    // 尽量容许其余乱写
    // 返回的结果和promise是同一个那么永远不会成功和失败
    if (promise2 === x) {
        throw new Error('不能反悔本身')
    }

    let called;
    // 判断Promise类型
    if (typeof x !== 'object' && (typeof x === 'object' || typeof x === 'function')) {
        try {


            let then = x.then;
            // promise 能够是一个函数。函数上有一个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 {
                // 不是函数,又是一个promise
                resolve(x)
            }


        } catch (err) {
            if (called) return
            called = true;
            reject(err);
        }

    } else {
        //通常类型返回
        //3.若是then中不管是成功的回调仍是失败的回调只要返回告终果就会走下一个then中的成功,
        resolve(x);
    }
}
复制代码
Promise.prototype.then = function (onFulfilled, onRjected) {
    let self = this;

    let promise2 = null;

    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function (value) {
        return value;
    };
    onRjected = typeof onRjected === 'function' ? onRjected : function (reason) {
        throw reason;
    }


    if (self.status === FULFILLED) {
        promise2 = new Promise(function (onFulfilled2, onRjected2) {
            setTimeout(function () { //9.promise规范中要求,全部的onFufiled和onRjected都须要异步执行,setTimeout
                try {
                    let result = onFulfilled(self.value);
                    resolvePromise(promise2, result, onFulfilled, onRjected);
                } catch (err) {
                    onRjected2(err);//若是有错误走下一个then的失败

                }
            });

        });
    }
    if (self.status === REJECTED) {
        promise2 = new Promise(function (onFulfilled2, onRjected2) {
            setTimeout(function () { //9.promise规范中要求,全部的onFufiled和onRjected都须要异步执行,setTimeout
                try {
                    let reason = onRjected(self.reason);
                    resolvePromise(promise2, reason, onFulfilled, onRjected);
                } catch (err) {
                    onRjected2(err);//若是有错误走下一个then的失败

                }
            });
        });

    }

    if (self.status === PENDING) {
        promise2 = new Promise(function (resolve, reject) {
            self.onResolvedCallbacks.push(function () {
                setTimeout(function () { //9.promise规范中要求,全部的onFufiled和onRjected都须要异步执行,setTimeout
                    try {
                        let value = onFulfilled(self.value);
                        resolvePromise(promise2, value, resolve, reject);
                    } catch (err) {
                        reject(err); //若是有错误走下一个then的失败
                    }
                });
            });

            self.onRejectedCallbacks.push(function () {
                setTimeout(function () { //9.promise规范中要求,全部的onFufiled和onRjected都须要异步执行,setTimeout
                    try {
                        let reason = onRjected(self.reason);
                        resolvePromise(promise2, reason, resolve, reject);
                    } catch (err) {
                        reject(err);//若是有错误走下一个then的失败

                    }
                });

            })
        });

    }
    return promise2;
}
复制代码

实现过程(五)

实现catch, all, race, resolve, reject, defer这些事例方法。框架

// 捕获错误的方法
// 至关于不要then的第一个成功回调
Promise.prototype.catch = function (callback) {
    return this.then(null, callback)
};

Promise.prototype.all = function (promises) {
    return new Promise(function (resolve, reject) {
        let returns = [];
        let index = 0;

        function processData(i, data) {
            returns[i] = data;
            if (++index === promises.length) {
                resolve(returns)
            }
        }

        for (let i = 0; i <= promises.length; i++) {
            promises[i].then(function (data) {
                processData(i, data)
            }, reject);
        }
    })

};

Promise.prototype.race = function (promises) {
    return new Promise(function (resolve, reject) {
        promises.forEach(function (value) {
            value.then(resolve, reject);
        })
    })
};

Promise.resolve = function (value) {
    return new Promise(function (resolve, reject) {
        resolve(value)
    });
};

Promise.reject = function (reason) {
    return new Promise(function (resolve, reject) {
        reject(reason)
    })
};

Promise.defer = Promise.deferred = function () {
    let dfd = {};
    return new Promise(function (resolve, reject) {
        dfd.resolve = resolve;
        dfd.reject = reject;
    });
    return dfd;
};
复制代码

Genernator

Genernator函数要用* 来比标识,yield(暂停 产出),它会将函数分割出好多个部分,调用一次next就会继续向下执行,返回结果是一个迭代器,迭代器有一个next方法,yield后面跟着的是value的值,yield等号前面的是咱们当前调用next传进来的值,第一次next传值是无效的。异步

function* read() {
    console.log('init');
    let a = yield '第一波汤圆';
    console.log(a);
    let b = yield '第二波汤圆'
    console.log(b);
    return b;
}
let it = read();
console.log(it.next('给我汤圆'));
// init
// {value:'第一波汤圆',done:false}
console.log(it.next('我还要汤圆')); 
// 我还要汤圆
// {value:'第二波汤圆',done:false}
console.log(it.next('够了不要了'));
// 够了不要了
// {value:‘第二波汤圆’,done:true}
console.log(it.next('还有么?'));
// {value:undefined,done:true}
复制代码

能够看到,当调用read()时候,程序仅仅返回一个迭代器。函数内代码并无运行。async

提一次调用next('给我汤圆'),后函数运行了,以下内容:

console.log('init');
yield '第一波汤圆';
// 因此答应出来
// init
// 而且返回{value:'第一波汤圆',done:false}
// 注意,第一次传入的'给我汤圆' 函数并无接收处理。
复制代码

第二次调用console.log(it.next('我还要汤圆')),程序运行了以下内容:

let a = '我还要汤圆';
console.log(a);
yield '第二波汤圆'
// 我还要汤圆
// {value:'第二波汤圆',done:false}
// 将传入参宿赋值给a,并打印出来,而后返回yield后的'第二波汤圆'
复制代码

第三次调用console.log(it.next('够了不要了')),程序运行了以下内容:

let b = '够了不要了'
console.log(b);
return b;
// 够了不要了
// {value:‘够了不要了’, done:true}
// 将传入参宿赋值给b,并打印出来,而后返回b的'够了不要了',这是迭代结束,返回的done就变为true。
复制代码

继续调用console.log(it.next('还有么?')),程序没有迭代内容了,因此直接返回

// {value:undefined, done:true}
复制代码

Generator主要和Promise搭配使用

let bluebird = require('bluebird'); // bluebird见相关函数库介绍
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);
function* gRead() {
    let content1 = yield read('./1.txt', 'utf8');
    // content1 存放的是content2的文件地址
    let content2 = yield read(content1, 'utf8');
    return content2;
}
// 接下来我要一次一次的调用next才能获得我要的content2的内容,麻烦
复制代码

因而与co库

// co库 npm install co 能够自动的将generator进行迭代
let co = require('co');
co(gRead()).then(function (data) {
    console.log(data)
})
// 这样就能够一次拿到结果
复制代码

但是co库是在么实现的呢?让咱们来模拟一个co库

function co(it) {
    // 必须返回一个promise
    return new Promise(function (resolve, reject) {
        function next(d) { // 第二次递归进来就有值了。
            let { value, done } = it.next(d);
            // value 是一个promise,第一次调用传递值无用。
            if (!done) {
                // 若是没有遍历结束
                value.then(function (data) {
                // content2的promise运行,并传入读取结果。
                    next(data)
                }, reject)
            } else {
                resolve(value); 
            }
        }
        next();
    });
}
复制代码

能够看出co库就是递归调动了迭代器里的每个next(),直到done===true。

async-await

async-await能够理解为co + generator的语法糖。用async 来修饰函数,aysnc须要配await,await只能promise。

async function aRead(){
    try{
        let content1 = await read('./1.txt','utf8');
        let content2 = await read(content1,'utf8');
        return content2;
    }catch(e){ // 若是出错会catch
        console.log('err',e)
    }
}
复制代码

函数调用和promise同样,以下:

// async函数返回的是promise,
aRead().then(function (data) {
    console.log('print', data);
}, function (err) {
    console.log('error', err);
});
复制代码

async/await解决了哪些问题呢?

  • 回调地狱。
  • 并发执行异步,在同一时刻同步返回结果 Promise.all。
  • 解决了返回值的问题,yield返回的要靠调用函数传入。
  • 能够实现代码的try/catch。

相关函数库

co

bluebird

/ blueBird
// npm install bluebird
let fs = require('fs');
let bluebird = require('bluebird');
function promisify(fn) { // promise化 将回调函数在内部进行处理
    return function (...args) {
        return new Promise(function (resolve, reject) {
            fn(...args, function (err, data) {
                if (err) reject(err);
                resolve(data);
            })
        })
    }
}
function promisifyAll(obj) {
    Object.keys(obj).forEach(key => { // es5将对象转化成数组的方法
        if (typeof obj[key] === 'function') {
            obj[key + 'Async'] = promisify(obj[key])
        }
    })
}
promisifyAll(fs); // 将全部的方法所有增长一个promise化
fs.readFileAsync('./1.txt', 'utf8').then(function (data) {
    console.log(data);
});
复制代码

promises-aplus-tests

相关文章
相关标签/搜索