美文网首页
34-Promise

34-Promise

作者: 早起的鸟儿 | 来源:发表于2019-11-05 15:31 被阅读0次
    一、Promise概述

    Promise是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。ES6将其写进了语言标准,统一了用法,原生提供了Promise对象。

    所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise是一个对象,从它可以获取异步操作的消息。Promise提供统一的API,各种异步操作都可以用同样的方法进行处理。

    回调函数实现异步编程

    let fn = function(callback){
        console.log(111);
        setTimeout(function(){
            callback()
        },1000)
    }
    function b(){
        console.log(222)
    }
    fn(b)
    

    缺点就是当我嵌套多层的时候后期代码很难维护。

    二、Promise
    1. Promise格式
    var promise = new Promise(function(resolve, reject) {
      // ... some code
    
      if (/* 异步操作成功 */){
        resolve(value);  //可以传递参数,如value
      } else {
        reject(error);
      }
    });
    

    Promise构造函数接受一个函数(回调函数)作为参数,该函数的两个参数分别是resolve和reject。它们是两个函数,由JavaScript引擎提供,不用自己部署。

    resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功”(即从Pending变为Resolved),在异步操作成功时调用,并将异步操作的结果,作为参数传递出去;reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”(即从Pending变为Rejected),在异步操作失败时调用,并将异步操作报出的错误,作为参数传递出去。

    Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数。then方法是为promise对象添加状态时的回调函数

    promise.then(function(value) {  //接收res()函数传递过来的值
      // success
    }, function(error) {
      // failure
    });
    

    then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Reject时调用。其中,第二个函数是可选的,不一定要提供。这两个函数都接受Promise对象传出的值作为参数。

    promise的then方法的第二个参数和catch区别:

    1. 如果在then的第一个函数里抛出了异常,后面的catch能捕获到,而then的第二个函数捕获不到。
    2. then的第二个参数和catch捕获错误信息的时候会就近原则,如果是promise内部报错,reject抛出错误后,then的第二个参数和catch方法都存在的情况下,只有then的第二个参数能捕获到,如果then的第二个参数不存在,则catch方法会捕获到。

    所以Promise的完整用法就是:

    var promise = new Promise(function(resolve, reject) {
      // ... some code
    
      if (/* 异步操作成功 */){
        resolve(value);
      } else {
        reject(error);
      }
    });
    
    promise.then(function(value) {
      // success
    }, function(error) {
      // failure
    }).catch(error){
      console.log(error)
    }
    

    then和catch也创建并返回一个新的 Promise,它可以 用于表示 Promise 链流控制

    1. 多个Promise的格式:
    let fn = function(){
        
        return new Promise((res,rej)=>{ 
            // ... some code
            res("易驰es6")   //可以传递参数
        })
    }
    fn().then((res)=>{
        return new Promise(function(res,rej){  //第二次Promise
            // ... some code
            res()
        })
    }).then(()=>{
        return new Promise(function(res,rej){
            res()
        })
    }).then(()=>{
        console.log("第三次Promise") //可以无限次的添加Promise
    })
    

    注意:Promise新建后就会立即执行,👇举例来说明:

    举个🌰

    {
        let fn = function(){
            console.log(111);
            return new Promise((res,rej)=>{ 
                console.log(555);
                res()
            })
        }
        fn().then(()=>{
            console.log(444)
        })
        console.log(333)
    
        //111 
        //555
        //333
        //444
    }
    

    fn()被调用以后先输出“111”,接着Promise新建后立即执行,所以首先输出的是“555”。然后,输出“333”,then方法指定的回调函数,将在当前脚本所有同步任务执行完才会执行,所以“444”最后输出

    但如果加入定时器会有不一样的结果,👇
    再来个🌰

    {
        let fn = function(){
            console.log(111);
            return new Promise((res,rej)=>{ 
                setTimeout(() => {
                    console.log(222)
                }, 1000);
                res()
            })
        }
        fn().then(()=>{
            console.log(444)
        })
    
        //111
        //444
        //222
    }
    

    当有两个Promise和定时器的时候:👇
    举个🌰:

    {
        let fn = function(){
            console.log(111);
            return new Promise((res,rej)=>{
                setTimeout(() => {
                    console.log(222)
                }, 1000);
                res()
            })
        }
        fn().then(()=>{
            console.log(444);
            return new Promise(function(res,rej){  //嵌套第二层Promise
                setTimeout(()=>{
                    console.log('aaa')
                },1000)
                res()
            })
        }).then(()=>{
            console.log('bbb')
        })
    
        //111 444 bbb 222 aaa
    }
    

    上面代码中,fn()先执行输入111,然后遇到Promise,本应该立即输出,可是遇到了定时器,所以会先把定时器放入执行队列中等待执行,此时会执行then里边的回调函数,输出444,输出以后再次遇到Promise,本应该立即输出但是同样遇到了定时器,依然是先把定时器放入到执行队列中等待执行,所以不会输出aaa,而是去执行了then回调函数输入了bbb,直到此时剩下两个定时器等待执行,会根据定时器的毫秒数来执行定时器。会先执行快要到时的那个定时器,因此上述例子两个定时器都是1秒以后执行,所以会先执行先放入队列的那个定时器。

    假如定时器的毫秒数不一样时:👇
    再举个🌰

    {
        let fn = function(){
            console.log(111);
            return new Promise((res,rej)=>{ 
                setTimeout(() => {
                    console.log(222)
                }, 5000);
                res()
            })
        }
        fn().then(()=>{
            console.log(444);
            return new Promise(function(res,rej){  //嵌套第二层Promise
                setTimeout(()=>{
                    console.log('aaa')
                },1000)
                res()
            })
        }).then(()=>{
            console.log('bbb')
        })
        //111 444 bbb aaa 222
    }
    

    前边的三个输出结果依然不变,当5秒定时器和1秒定时器,会先执行1秒定时器再执行五秒定时器。

    总结:当函数中包括Promise,调用函数先执行Promise之前的代码,当遇到Promise立即执行Promise里边的代码,将在当前脚本所有同步任务执行完才会执行,then方法指定的回调函数,如果含有定时器定时器代码将按顺序执行。

    定时器总结:定时器属于异步操作,遇到定时器会先把定时器放入等待执行的队列中,等到队列中的哪个定时器到了时间以后就会自动执行。其他同步代码的执行完以后按顺序执行定时器。

    最后来个🌰

    {
        let fn = function(){
            console.log(111);
            return new Promise((res,rej)=>{ 
                setTimeout(() => {
                    console.log(222)
                }, 5000);
                res()
            })
        }
        fn().then(()=>{
            console.log(444);
            return new Promise(function(res,rej){  //嵌套第二层Promise
                setTimeout(()=>{
                    console.log('aaa')
                },1000)
                res()
            })
        }).then(()=>{
            console.log('bbb')
        })
        console.log("sss");   
        //111 sss 444 bbb aaa 222
    }
    
    三、Promise方法
    1. Promise.all([p1,p2...])
      Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例,同时执行,并发的状态,要可以都可以,要不可以都不可以。
    {
        let loadImg = function(srcUrl){
            return new Promise((res,rej)=>{
                let imgs = document.createElement("img")
                imgs.src = srcUrl;
                imgs.onload = function(){
                    res(imgs)
                }
            })
        }
        Promise.all([
            loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1345259982,1021899038&fm=26&gp=0.jpg'),
            loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1608765616,2201444842&fm=26&gp=0.jpg'),
            loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1345259982,1021899038&fm=26&gp=0.jpg'),
            loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1608765616,2201444842&fm=26&gp=0.jpg')
        ]).then(function(img){
            img.forEach(function(item){
                document.body.appendChild(item)
            })
        }) 
    }
    

    就是四个调用同时执行,要成功都成功。并发执行。

    1. Promise.race()
      Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。它和all的区别就是优先加载一个,哪个速度比较快显示哪个
    {
        let loadImg = function(srcUrl){
            return new Promise((res,rej)=>{
                let imgs = document.createElement("img")
                imgs.src = srcUrl;
                imgs.onload = function(){
                    res(imgs)
                }
            })
        }
        Promise.race([
            loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1345259982,1021899038&fm=26&gp=0.jpg'),
            loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1608765616,2201444842&fm=26&gp=0.jpg'),
            loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1345259982,1021899038&fm=26&gp=0.jpg'),
            loadImg('https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1608765616,2201444842&fm=26&gp=0.jpg')
        ]).then(function(img){
            document.body.appendChild(img)
        }) 
    }
    

    相关文章

      网友评论

          本文标题:34-Promise

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