手写实现Promise

# 手写实现Promise

## Promise异步编程

  异步编程简介:无论在浏览器环境还是node环境都需要JavaScript的异步编程,如在浏览器环境中的定时器、事件、ajax等或是node环境中的文件读取、事件等。伴随着异步编程就有回调机制,异步编程免不了回调。

异步编程问题:产生回调地狱,难于维护和扩展。

       try、catch只能捕捉同步代码中出现的异常。

       同步并发的异步操作存在一定的问题。

解决方案: ES6 Promise可以解决回调地狱,以及同步并发的异步问题。

  jQuery的Callbacks和Lodash的after都是解决回调问题的其他方法

### Promise使用

```js

//excutor function 同步执行

let promise = new Promise((resolve,reject)=>{

    //异步操作

    setTimeout(()=>{

        Math.random()*100 > 60 ? resolve("ok"): reject("fail");

    },1000);

});

//Promise内部状态,pending(等待),onFulFilled(成功),onReject(失败)

//注册回调,异步执行

//宏任务(setTimeout)    微任务

//微任务有优先执行权

//then 可以链式操作

//上一个then不抛出错误的话,下一个then会执行成功函数

//返回值作为下一个then注册函数的执行参数

//如果返回值为Promise对象,则下一个then的执行取决于该对象的执行函数

promise.then((val)=>{//微任务

    console.log(val);

    return new Promise((resolve,reject)=>{

        reject("newPromise:fail");

    });

},(reason)=>{

    console.log(reason);

    return "fail then1:param";

}).then((val)=>{

    console.log("ok then2:",val);

},(reason)=>{

    console.log("fail then2:",reason);

});

```

#### then 注册回调返回值

#### catch 异常捕获

```js

let promise = new Promise((resolve,reject)=>{

    throw new Error("test error");

});

//失败函数捕获

promise.then(null,(reason)=>{

    console.log(reason);

});

//链式调用时如果有空then,则相当于不存在可忽视

//catch捕获

//catch后面可以继续链式调用

promise.then().catch((error)=>{

    console.log(error);

    }).then((val)=>{

        console.log(val,"after catch: ok");

    },(reason)=>{

        console.log(reason,"after ctach: fail");

    })

```

#### finally 最后处理函数

#### Promise.all 同步并发异步的结果

```js

let oPro = new Promise(()=>{});

//Promise.all参数为数组,数组元素必须为Promise对象,其会将

//多个Promise实例包装成一个新的Promise实例。

//全部成功时数组内元素的返回值组成数组,只要有失败时返回最先被reject失败

//状态的值

Promise.all([oPro,oPro,oPro]).then((val)=>{

    console.log(val);//val为数组

});

```

#### Promise.race 谁先成功处理谁

```js

let oPro = new Promise(()=>{});

//Promise.race([p1,p2,p3]);里面的哪个结果获得的快,就返回那个结果,

//不管结果本身成功或失败。谁的状态先发生改变就返回谁的状态

Promise.race([oPro,oPro,oPro]).then((val)=>{

    console.log(val);

},(reason)=>{

    console.log(reason);

});

```

### Promise模拟实现

点击查看 [Promise规范][promise-standard]

```js

//考虑兼容性,用ES5实现

function MyPromise(excutor) {

    this.status = "pending";

    this.resolveValue = null;

    this.rejectReason = null;

    this.resolveCallbackList = [];

    this.rejectCallbackList = [];

    try {

        excutor(this.resolve.bind(this), this.reject.bind(this));

    } catch (e) {

        this.reject(e);

    }

}

MyPromise.prototype = {

    resolve(val) {

        if (this.status === "pending") {

            this.status = "FulFilled";

            this.resolveValue = val;

            this.resolveCallbackList.forEach(function (cbFn) {

                cbFn();

            });

        }

    },

    reject(reason) {

        if (this.status === "pending") {

            this.status = "Rejected";

            this.rejectReason = reason;

            this.rejectCallbackList.forEach(function (cbFn) {

                cbFn();

            });

        }

    },

    then(onFulFilled, onRejected) {

        var self = this;

        self._dealNullThen(onFulFilled)._dealNullThen(onRejected);

        return new MyPromise(function (resolve, reject) {

            if (self.status === "FulFilled") {

                //模拟异步执行,此为宏任务,底层代码为微任务

                setTimeout(function () {

                    try {

                        var nextResolveValue = onFulFilled(self.resolveValue);

                        self._dealReturnValPromse(nextResolveValue, resolve, reject);

                    } catch (e) {

                        reject(e);

                    }

                }, 0);

            } else if (self.status === "Rejected") {

                setTimeout(function () {

                    try {

                        var nextRejectValue = onRejected(self.rejectReason);

                        self._dealReturnValPromse(nextRejectValue, resolve, reject, true);

                    } catch (e) {

                        reject(e);

                    }

                }, 0);

            } else if (self.status === "pending") {

                self.resolveCallbackList.push(function () {

                    setTimeout(function () {

                        try {

                            var nextResolveValue = onFulFilled(self.resolveValue);

                            self._dealReturnValPromse(nextResolveValue, resolve, reject);

                        } catch (e) {

                            reject(e);

                        }

                    }, 0);

                });

                self.rejectCallbackList.push(function () {

                    setTimeout(function () {

                        try {

                            var nextRejectValue = onRejected(self.rejectReason);

                            self._dealReturnValPromse(nextRejectValue, resolve, reject, true);

                        } catch (e) {

                            reject(e);

                        }

                    }, 0);

                });

            }

        });

    },

    _dealNullThen(fn) { //处理空then情况

        if (!fn) {

            fn = function (val) {

                return val;

            }

        }

        return this;

    },

    _dealReturnValPromse(returnVal, resolve, reject, isRejected) {

        if (returnVal instanceof MyPromise) {

            //若返回值为MyPromise对象,则后面的执行状态由该对象来决定

            returnVal.then(function (val) {

                resolve(val);

            }, function (reason) {

                reject(reason);

            });

        } else {

            //如果返回值不为MyPromise对象,则执行回调函数

            if (!isRejected) {

                resolve(returnVal);

            } else {

                reject(returnVal);

            }

        }

    },

};

MyPromise.race = function (promiseArr) {

    return new MyPromise(function (resolve, reject) {

        promiseArr.forEach(function (ele) {

            ele.then(resolve, reject);

        });

    });

};

//全部成功才执行成功回调,只要有一个失败就执行失败回调

MyPromise.all = function (promiseArr) {

    return new MyPromise(function (resolve, reject) {

        var returnValueArr = [],

            count = 0;

        for (var i = 0, len = promiseArr.length; i < len; i++) {

            (function (i) {

                promiseArr[i].then(function (val) {

                    returnValueArr[i] = val;

                    if (++count == len) {

                        resolve(returnValueArr);

                    }

                }, function (reason) {

                    reject(reason);

                });

            }(i));

        }

    });

};

```

## ES6 Symbol

数据结构:第七种数据结构Symbol

特点:唯一,可作为对象的属性,有静态属性Symbol.iterator

## ES6 Iterator

&emsp;&emsp;**迭代器目的**:标准化迭代操作。

&emsp;&emsp;**迭代模式**:提供一种方法可以顺序获得聚合对象中的各个元素,是一种最简单也是最常见的设计模式。它可以让用户透过特定的接口巡访集合中的每一个元素而不用了解底层的实现。

&emsp;&emsp;**迭代器简介**:依照与迭代模式的思想而实现,分内部迭代器和外部迭代器。

&emsp;&emsp;&emsp;&emsp;**内部迭代器**:本身是函数,该函数内部定义好迭代规则,完全接受整个迭代过程,外部只需要一次初始调用。

&emsp;&emsp;&emsp;&emsp;Array.prototype.forEach、jQuery.each内部迭代器

&emsp;&emsp;&emsp;&emsp;**外部迭代器**:本身是函数,执行返回迭代对象,迭代下一个元素必须显式调用,调用复杂度增加,但灵活性增强。

&emsp;&emsp;&emsp;&emsp;function outerIterator(){}外部迭代器

```js

//模拟写自己外部迭代器

function OuterIterator(o){

    let curIndex=0;

    let next=()=>{

        return {

            value:o[curIndex],

            done:o.length == ++curIndex,

        }

    }

    return {

        next,

    }

}

let arr=[1,2,3];

let oIt=outerIterator(arr);

oIt.next();

oIt.next();

oIt.next();

```

### 部署Iterator

```js

let obj={

    0:"a",

    1:"b",

    2:"c",

    length:3,

    //要能迭代,必须部署Iterator,符合ES6

    [Symbol.iterator]:function (){

        let curIndex=0;

        let next = () => {

            return {

                value: this[curIndex],

                done: this.length == curIndex++,

            }

        };

        return{

            next,

        }

    },

}

console.log([...obj]);

```

### Generator

Generator简介:生成器,本身为函数,执行后返回迭代对象,函数内部要配合yield使用,Generator函数分段执行,遇到yield即暂停。

特点:

&emsp;&emsp;function和函数名之间需要带*

&emsp;&emsp;函数体内yield表达式,产出不同的内部状态(值)

```js

//示例 Generator产生迭代对象

function *test(){

    let val1= yield "a";

    console.log(val1);//val1的值为第二次next中传入的值

    yield "b";

    yield "c";

    return "d";

}

let oG=test();

oG.next();//{value:"a",done:false}

oG.next();//{value:"b",done:false}

oG.next();//{value:"c",done:false}

oG.next();//{value:"d",done:true}

```

改造前面的代码

```js

let obj={

    0:"a",

    1:"b",

    2:"c",

    length:3,

    //要能迭代,必须部署Iterator,符合ES6

    [Symbol.iterator]:function (){

        let curIndex=0;

      while(curIndex != this.length){

          yield this[curIndex++];

      };

    },

}

console.log([...obj]);

```

Generator函数使用

```js

function *read(path){

    let val1 = yield readFile(path);

    let val2 = yield readFile(val1);

    let val3 = yield readFile(val2);

    return val3;

}

let oG = read();

let {value, done} = oG.next();

value.then((val)=>{

    let {value, done} = oG.next();

    value.then((val)=>{

        let {value, done} = oG.next();

        value.then((val)=>{

            console.log(val);

        });

    });

});

//递归优化

function Co(oIterator){

    return new Promise((res,rej)=>{

        let next = (data)=>{

            let {value, done} = oIterator.next(data);

            if(done){

                res(value);

            }else{

                value.then((val)=>{

                    next(val);

                },rej);

            }

        };

        next();

    });

}

//使用

Co(read()).then((val)=>{

    console.log(val);

});

```

#### Promise化

```js

let fs = require("fs");

let path="./data.txt";

let format="utf-8";

//原始函数

function readFile(){

    return new Promise((res,rej)=>{

        fs.readFile(path,format,(err,data)=>{

                if(err){

                    rej(err);

                }else{

                    res(data);

                }

            });

    });

}

//对函数进行promise化  (npm i bluebird)

function promisify(fn){

    return (...arg)=>{

        return new Promise((res,rej)=>{

            fn(...arg,(err,data)=>{

                if(err){

                    rej(err);

                }else{

                    res(data);

                }

            });

        });

    };

}

let readFilePromisify=promisify(fs.readFile);

readFilePromisify(path,format).then((val)=>{

    console.log(val);

});

//进一步对对象内异步方法进行promise化

function promisifyAll(){

    for(let key in obj){

        let fn=obj[key];

        if(typeof fn === "function"){

            obj[key + "Async"] = promisify(fn);

        }

    }

}

promisifyAll(fs);

fs.readFileAsync(path,format).then((val)=>{

    console.log(val);

});

```

### async & await

async简介:async函数,是Generator语法糖,通过babel编译后可以看出它就是Generator+Promise+Co(递归)思想实现的,配合await使用。

目的:优雅的解决异步操作问题。

```js

//解决回调地狱

//try catch

//同步并发的异步结果

async function read(path){

    try{

        let val1 = await readFile(path);

        let val2 = await readFile(val1);

        let val3 = await readFile(val2);

    }catch(e){

        console.log(e);//能够捕获异常

    }

    return val3;

}

read(path).then((val)=>{

    console.log(val);

});

//解决同步并发的异步问题

//Promise.all有局限性,一个异常其他也不能出结果

Promise.all([readFile(path1),readFile(path2),readFile(path3)])

.then((val)=>{

    console.log(val);

},(reason)=>{

    console.log(reason);

});

//使用async和await可以解决

```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345