深入EcmaScript6 Promise,并手写实现。

首先,先来看Promise用法,Promise主要用于异步,在之前的JS,除了上传图片,以及Ajax,几乎没有要用到异步的地方。但是Node.JS的出现,改变了这一现象,因为在Node出现的时候,后端语言,Java,Python,PHP已经占据了后端的主要份额,要想脱引而出,就要有一些其他语言没有的特点,Node的特点,就是大量回调来处理并发, 所以会用到大量的回调函数,Promise就登场了,当然,ES7出现的asyns和await更是将回调完美的展现出来,这点在Koa2中,大量运用。Promise究竟为何物,我们先来揭开Promise的用法吧:

不用Promise的情况:

                const ajax = function(callback){
                    console.log("start");
                    setTimeout(()=>{
                        callback&&callback.call();
                    },1000)
                }
                ajax(function(){
                    console.log("Hello World");
                })

我们定义一个ajax方法,里面传入一个方法,用setTimeout模拟发送请求的操作,然后在一秒钟,输出这个方法,结果也输出正确。

Promise基本使用:

                const ajax = function(){
                    console.log("start");
                    return new Promise(function(resolve,reject){
                        setTimeout(()=>{
                            resolve("hello");
                        },1000)
                    })
                }
                ajax().then((value)=>{
                    console.log(value,"world");
                })

和上文 一样,只不过这次是返回一个Promise对象,Promise对象可以链式调用then方法,进行下一步操作。

Promise链式调用:

                const ajax = function(){
                    console.log("start");
                    return new Promise(function(resolve,reject){
                        setTimeout(function(){
                            resolve()
                        },1000);
                    })
                }
                ajax().then(function(){
                    return new Promise(function(resolve,reject){
                        setTimeout(function(){
                            resolve()
                        },2000);
                    })
                }).then(()=>{
                    console.log("Hello World");
                })

注意,then方法,会默认返回一个Promise对象,比如return 1,这个1,就对应在下次then(value=>{})的value值,但是这样功能太少了,比如说抛出错误,不好维护,所以还是应该写return new Promise();

Promise链式调用的复杂情形:

new Promise(resolve=>{
                    console.log("start1");
                    setTimeout(()=>{
                        resolve(100);
                    },1000);
                })
                .then(value=>{
                    return new Promise(resolve=>{
                        console.log("start2");
                        console.log(value);
                        setTimeout(()=>{
                            resolve(200);
                        },1000);
                    })
                })
                .then(value=>{
                    return new Promise(resolve=>{
                        console.log("start3");
                        console.log(value);
                        setTimeout(()=>{
                            resolve(300);
                        },1000);
                    })
                })

第一个方法,返回的结果参数,会被第二个方法执行,第二个方法返回的参数会被第三个执行。

Promise报错的处理:

const ajax = function(num){
                    console.log("start");
                    return new Promise(function(resolve,reject){
                        if(num>5){
                            resolve();
                        }else{
                            throw new Errow("出错了");
                        }
                    })
                }
                ajax(3).then(()=>{
                    console.log("Hello World");
                }).catch((err)=>{
                    console.log(err);
                })

建议即使只需要then,还是应该写catch,捕获异常。

Promise高级篇:

所有图片加载完再添加到页面:

function loadImg(src) {
                    return new Promise((resolve, reject) => {
                        let img = document.createElement('img');
                        img.src = src;
                        img.onload = function() {
                            resolve(img);
                        }
                        img.onerror = function(err) {
                            reject(err);
                        }
                    })
                }
                function showImgs(imgs) {
                    imgs.forEach(function(img) {
                        document.body.appendChild(img);
                    })
                }
                Promise.all([
                    loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449634314&di=56a92121182ba40c4a640c053dc0c64b&imgtype=0&src=http%3A%2F%2Fb.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F9825bc315c6034a8ef5250cec5134954082376c9.jpg'),
                    loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449650753&di=5642d7deef9e1e63b6f3868c75b625f0&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2Fc2cec3fdfc03924590b2a9b58d94a4c27d1e2500.jpg'),
                    loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449650745&di=6d65c12f144a14bb2593901da1e995a1&imgtype=0&src=http%3A%2F%2Fh.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F0b46f21fbe096b63491b16ea06338744ebf8ac0e.jpg')
                ]).then(showImgs)

有一个图片加载完就添加到页面:

function loadImg(src){
                    return new Promise((resolve,reject)=>{
                      let img=document.createElement('img');
                      img.src=src;
                      img.onload=function(){
                        resolve(img);
                      }
                      img.onerror=function(err){
                        reject(err);
                      }
                    })
                  }
                
                  function showImgs(img){
                    let p=document.createElement('p');
                    p.appendChild(img);
                    document.body.appendChild(p)
                  }
                
                  Promise.race([
                    loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449634314&di=56a92121182ba40c4a640c053dc0c64b&imgtype=0&src=http%3A%2F%2Fb.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F9825bc315c6034a8ef5250cec5134954082376c9.jpg'),
                    loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449650753&di=5642d7deef9e1e63b6f3868c75b625f0&imgtype=0&src=http%3A%2F%2Fg.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2Fc2cec3fdfc03924590b2a9b58d94a4c27d1e2500.jpg'),
                    loadImg('https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1559449650745&di=6d65c12f144a14bb2593901da1e995a1&imgtype=0&src=http%3A%2F%2Fh.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%210b46f21fbe096b63491b16ea06338744ebf8ac0e.jpg')
                  ]).then(showImgs)

实现队列:

              function queue(things) {
                    let promise = Promise.resolve();
                    things.forEach(element => {
                        promise = promise.then(() => {
                            return new Promise(resolve => {
                                setTimeout(() => {
                                    console.log(element)
                                    resolve('ok');
                                }, 1000);
                            });
                        })
                    });
                }
                queue(['a', 'b', 'c']);

可依靠这个实现JQuery的animate队列。

ES7写法:

                async function queue(arr) {
                  let res = null
                  for (let promise of arr) {
                    res = await promise(res)
                  }
                  return await res
                }
                queue(["a", "b", "c"])
                  .then(data => {
                    console.log(data)// abc
                  })

Promise.reduce也可以用来顺序执行函数,但是可使用的场景非常有限,一般用来读取文件信息。

手写Promise实现:

                const PENDING = "pending";
                const RESOLVED = "resolved";
                const REJECTED = "rejected";
                function MyPromise(fn){
                    let that = this;
                    that.state = PENDING;
                    that.value = null;
                    that.resolvedCallbacks = [];
                    that.rejectedCallbacks = [];
                    function resolve(value){
                        if(that.state === PENDING){
                            that.state = RESOLVED;
                            that.value = value;
                            that.resolvedCallbacks.map(callback=>callback(that.value))
                        }
                    }
                    function reject(value){
                        if(that.state === PENDING){
                            that.state = REJECTED;
                            that.value = value;
                            that.rejectedCallbacks.map(callback=>callback(that.value))
                        }
                    }
                    try{
                        fn(resolve,reject)
                    }catch(e){
                        reject(e)
                    }
                }
                
                
                MyPromise.prototype.then = function(onFulfilled,onRejected){
                    let that = this;
                    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
                    onRejected = typeof onRejected === 'function' ? onRejected : r=>{throw r};
                    if(that.state === PENDING){
                        that.resolvedCallbacks.push(onFulfilled)
                        that.rejectedCallbacks.push(onRejected)
                    }
                    if(that.state === RESOLVED){
                        onFulfilled(that.value);
                    }
                    if(that.state === REJECTED){
                        onRejected(that.value);
                    }
                }
                
                const ajax = function(){
                    console.log("start");
                    return new MyPromise(function(resolve,reject){
                        setTimeout(()=>{
                            resolve("hello");
                        },1000)
                    })
                }
                ajax().then((value)=>{
                    console.log(value,"world");
                })
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • Promise 对象 Promise 的含义 Promise 是异步编程的一种解决方案,比传统的解决方案——回调函...
    neromous阅读 8,698评论 1 56
  • 官方中文版原文链接 感谢社区中各位的大力支持,译者再次奉上一点点福利:阿里云产品券,享受所有官网优惠,并抽取幸运大...
    HetfieldJoe阅读 8,676评论 0 29
  • Promiese 简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果,语法上说,Pr...
    雨飞飞雨阅读 3,348评论 0 19
  • 目录:Promise 的含义基本用法Promise.prototype.then()Promise.prototy...
    BluesCurry阅读 1,488评论 0 8
  • 一、Promise的含义 Promise在JavaScript语言中早有实现,ES6将其写进了语言标准,统一了用法...
    Alex灌汤猫阅读 818评论 0 2