美文网首页
深入EcmaScript6 Promise,并手写实现。

深入EcmaScript6 Promise,并手写实现。

作者: LeeYaMaster | 来源:发表于2019-06-02 16:08 被阅读0次

    首先,先来看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");
                    })
    

    相关文章

      网友评论

          本文标题:深入EcmaScript6 Promise,并手写实现。

          本文链接:https://www.haomeiwen.com/subject/ttmxxctx.html