Promise的深入理解

一、promise 的认识

1.js的单线程

1)javascript的执行环境是单线程的
2)单线程:指JS引擎中负责解释和执行JavaScript代码的线程只有一个,也就是一次只能完成一项任务,这个任务执行完后才能执行下一个,它会「阻塞」其他任务,这个任务可称为主线程,但是实际上还有其他线程,如事件触发线程、ajax请求线程等

2.同步与异步

1)同步模式:也就是单线程模式,一次只能执行一个任务,函数调用后需等到函数执行结束,返回执行的结果,才能进行下一个任务,如果这个任务执行的时间较长,就会导致线程阻塞
2)异步模式:可以一起执行多个任务,函数调用后不会立即返回执行的结果,如果任务A需要等待,可先执行任务B,等到任务A结果返回后再继续回调

3.回调函数

1)回调函数:一段可执行的代码段,它以参数的形式传递给其他代码,在其合适的时间执行这段回调函数的代码,可以理解为执行完回来调用的函数
2)回调函数可以运用在同步调用,回调函数一般是最后执行的
实例代码:

   // 同步回调,回调函数一般是最后执行的
   //  before    3S后出现  callback  after
   // 将f2函数作为参数传入f1中,先执行输出before,判断f2是否是回调函数,再判断是否决定执行
   // f2是回调函数,3s后执行,输出callback,然后输出after
   var f1 = function(callback){

       console.log("before");
       (callback && typeof(callback) === "function") && callback();
       console.log("after");
   };

   var f2 = function(callback){

       var start = new  Date();
       while((new Date()-start)<3000){}
       console.log("callback");
   };
   f1(f2);

3)回调函数可以运行在异步调用,回调函数可能一段时间后执行或不执行,未达到执行的条件
实例代码:

    // 异步调用,可能一段时间后执行或不执行
    // 常见的异步回调有setTimeout()、setInterval(),还有ajax请求

    function request(url,parm,successFun,errorFun){
        $.ajax({
            type:'GET',
            url:url,
            parm:parm,
            async:true,
            success:successFun,
            error:errorFun
        });
    }

    request("test03.html","",function(data){
        // 请求成功,对请求回来的数据进行处理
       console.log("请求成功后回调",data);
    },function(error){
        // 请求失败,返回请求失败的信息
        console.log("请求失败了",error);
    });

4.promise的写法
1)Promise对象代表一个未完成、但预计将来会完成的操作 ,是一种异步的解决方案
它的三种状态为:
pending:初始值,不是fulfilled,也不是rejected
fulfilled:代表操作成功,解决
rejected:代表操作失败,拒绝
2)Promise有两种状态改变的方式,既可以从pending转变为fulfilled,也可以从pending转变为rejected。一旦状态改变,就「凝固」了,会一直保持这个状态,不会再发生变化。当状态发生变化,promise.then绑定的函数就会被调用
注意:Promise一旦新建就会立即执行,无法取消
3)实例如下:

     // promise是一个回调函数
    // resolve  异步操作执行成功后的回调函数,将Promise的状态置为fullfiled
    // reject  异步操作执行失败后的回调函数,将Promise的状态置为rejected
    var p = new Promise(function(resolve,reject){
        //  2s后输出     执行完成
        setTimeout(function(){
            console.log("执行完成");
            resolve("操作成功");
            reject("操作失败");
        },3000);
    });

4)优化,实际上Promise上的实例_promise是一个对象,不是一个函数,在声明的时候Promise传递的参数函数会立即执行。所以promise一般是包在一个函数中,在它的外层裹上一层函数,在需要的时候去运行这个函数
实例如下:

     // 创建了promise对象,在runAsync()函数中return返回promise对象
    //   2s后输出  执行完成
    function runAsync(){
        var p2 = new Promise(function(resolve,reject){

            setTimeout(function(){
                console.log("执行成功");
                resolve("操作成功");
                reject("操作失败");
            },3000);
        });
        return p2;
    }
    runAsync();

二、promise的用法

then的用法
1)语法:Promise.prototype.then(onFulfilled, onRejected)
2)对promise添加onFulfilled和onRejected回调,并返回的是一个新的Promise实例(不是原来那个Promise实例),且返回值将作为参数传入这个新Promise的resolve函数
3)实例如下:

 function runAsync(){
        var p2 = new Promise(function(resolve,reject){

            setTimeout(function(){
                console.log("执行成功");
                resolve("操作成功");
                reject("操作失败");
            },3000);
        });
        return p2;
    }
    runAsync();
 
   // Promise对象上有then、catch方法,在runAsync()的返回上直接调用then方法
    // then接收一个参数,是函数,并且会拿到我们在runAsync中调用resolve时传的的参数
    //  3s后输出   执行成功   操作成功
    runAsync().then(function(data){
       console.log(data);
    });

4)callback的实现

    // 封装回调函数
    // 2s后输出  执行成功  操作成功
    // 假如callback是一个异步操作,执行完成后有相应的回调函数,如果层层嵌套回调,会形成回调地狱
    // promise可以在then方法中继续写promise对象并且返回,继续调用then来进行回调操作
    function runAsync2(callback){

        setTimeout(function(){
            console.log("执行成功");
            callback("操作成功");
        },3000);
    }

    runAsync2(function(data){
       console.log(data);
    });

5)回调地狱
代码层层嵌套,环环相扣,很明显,逻辑稍微复杂一些,这样的程序就会变得难以维护,通过Promise构造函数才可以进行较好的解决
回调地狱的影响
代码臃肿
可读性差
耦合度过高,可维护性差
代码复用性差
容易滋生 bug
只能在回调里处理异常

链式操作的用法
1)promise,用维护状态、传递状态的方式来使得回调函数能够及时调用,它比传递callback函数要简单、灵活的多
2)实例如下:

    // runAsync1() 函数
    function runAsync1(){
        var p1 = new Promise(function(resolve,reject){
           setTimeout(function(){
               console.log("异步任务1执行完成");
               resolve("操作成功1");
               reject("操作失败");
           },3000);
        });
        return p1;
    }

    // runAsync2() 函数
    function runAsync2(){
        var p2 = new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log("异步任务2执行完成");
                resolve("操作成功2");
                reject("操作失败");
            },3000);
        });
        return p2;
    }

    // runAsync3() 函数
    function runAsync3(){
        var p3 = new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log("异步任务3执行完成");
                resolve("操作成功3");
                reject("操作失败");
            },3000);
        });
        return p3;
    }

  /*
    *  输出   异步任务1执行完成   操作成功1
    *         异步任务2执行完成   操作成功2
    *         异步任务3执行完成   操作成功3
    */
    //  每隔三秒就输出每个异步回调中的内容,通过promise的then方法,继续写promise对象并且返回
    //  继续调用then来进行回调操作,形成链式调用,不会形成回调地狱
    runAsync1().then(function(data){
        console.log(data);
        return runAsync2();
    }).then(function(data){
        console.log(data);
        return runAsync3();
    }).then(function(data){
        console.log(data);
    });

3)直接return数据而不是promise对象,在后面的then中就可以接收到数据了
实例如下:

   // runAsync1() 函数
   function runAsync1(){
       var p1 = new Promise(function(resolve,reject){
          setTimeout(function(){
              console.log("异步任务1执行完成");
              resolve("操作成功1");
              reject("操作失败");
          },2000);
       });
       return p1;
   }

   // runAsync2() 函数
   function runAsync2(){
       var p2 = new Promise(function(resolve,reject){
           setTimeout(function(){
               console.log("异步任务2执行完成");
               resolve("操作成功2");
               reject("操作失败");
           },3000);
       });
       return p2;
   }

   // runAsync3() 函数
   function runAsync3(){
       var p3 = new Promise(function(resolve,reject){
           setTimeout(function(){
               console.log("异步任务3执行完成");
               resolve("操作成功3");
               reject("操作失败");
           },3000);
       });
       return p3;
   }

 /*
    *  输出   异步任务1执行完成   操作成功1
    *         异步任务2执行完成   操作成功2
    *         返回数据
    */
   // 直接return数据而不是promise对象,在后面的then中就可以接收到数据了
   runAsync1().then(function(data){
       console.log(data);
       return runAsync1();
   }).then(function(data){
       console.log(data);
       return "返回数据";
   }).then(function(data){
      console.log(data);
   });

reject的用法
1)语法:Promise.reject(reason)
2)rejecte会让Promise对象立即进入rejected状态,并将错误对象传递给then指定的onRejected回调函数
3)reject会把Promise的状态置为rejected,这样我们在then中就能捕捉到,然后执行“失败”情况的回调
4)实例如下:

 function getNumber(){

       var p1 = new Promise(function(resolve,reject){

          setTimeout(function(){
             //  生成1-10的随机数
              var num = Math.ceil(Math.random()*10);
              //  成功后的回调
              if(num<5){
                  resolve(num);
              }else{
              // 失败后的回调
                  reject("数字不符合");
              }
          },3000);
       });
        return p1;

    }


    /*
    *  resolve时输出   操作成功  3
    *  reject时输出    操作失败  数字不符合
    */
    // getNumber函数用来异步获取一个数字,3秒后执行完成
    // 当数字小于5的时候,异步操作执行成功后的回调函数,将Promise的状态置为fullfiled
    // 当数字不小于5的时候,异步操作执行失败后的回调函数,将Promise的状态置为rejected
    // 运行getNumber并且在then中传了两个参数,then方法可以接受两个参数,第一个对应resolve的回调,第二个对应reject的回调
    getNumber().then(
            function(data){
                console.log("操作成功");
                console.log(data);
            },
            function(reject,data){
                console.log("操作失败");
                console.log(reject);
            }
    );

catch的用法
1)语法:Promise.prototype.catch(onRejected)
2)该方法是.then(undefined, onRejected)的别名,用于指定发生错误时的回调函数
3)catch它和then的第二个参数一样,用来指定reject的回调
实例如下:

function getNumber(){

       var p1 = new Promise(function(resolve,reject){

          setTimeout(function(){
             //  生成1-10的随机数
              var num = Math.ceil(Math.random()*10);
              //  成功后的回调
              if(num<5){
                  resolve(num);
              }else{
              // 失败后的回调
                  reject("数字不符合");
              }
          },3000);
       });
        return p1;

    }

    /*
     *  resolve时输出   操作成功  3
     *  reject时输出    操作失败  数字不符合
     */
    getNumber().then(function(data){
        console.log("操作成功");
        console.log(data);
    }).catch(function(data){
       console.log("操作失败");
        console.log(data);
    });

4)catch在执行resolve的回调时,也就是then的第一个参数,如果代码出错抛出异常,那么就会被catch捕获执行,catch的这个用法类似于 try/catch 的用法
实例如下:

 function getNumber(){

       var p1 = new Promise(function(resolve,reject){

          setTimeout(function(){
             //  生成1-10的随机数
              var num = Math.ceil(Math.random()*10);
              //  成功后的回调
              if(num<5){
                  resolve(num);
              }else{
              // 失败后的回调
                  reject("数字不符合");
              }
          },3000);
       });
        return p1;

    }

 /*
    *  resolve时输出   操作成功  1  操作失败   ReferenceError: data2 is not defined
    *   reject时输出    操作失败  数字不符合
    */
    //  代码出错抛出异常,进入到catch方法,把错误原因传到了data参数中
    getNumber().then(function(data){
        console.log("操作成功");
        console.log(data);
        // 未声明定义的变量 data2
        console.log(data2);
    }).catch(function(data){
       console.log("操作失败");
       console.log(data);
    });

all的用法
1)语法:Promise.all(iterable)
2)该方法用于将多个Promise实例,包装成一个新的Promise实例,Promise.all方法接受一个数组(或具有Iterator接口)作参数,数组中的对象(p1、p2、p3)均为promise实例(如果不是一个promise,该项会被用Promise.resolve转换为一个promise)。它的状态由这三个promise实例决定
3)当p1, p2, p3状态都变为fulfilled,p的状态才会变为fulfilled,并将三个promise返回的结果,按参数的顺序(而不是 resolved的顺序)存入数组,传给p的回调函数。当p1, p2, p3其中之一状态变为rejected,p的状态也会变为rejected,并把第一个被reject的promise的返回值,传给p的回调函数
4)all方法可以并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。也可以理解为可以并行执行多个异步操作,并且在一个回调中处理所有的返回数据。all是谁最慢,以谁为准执行回调
5)实例如下:

 function runAsync1(){
        var p1 = new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log("异步任务1执行完成");
                resolve("操作成功1");
                reject("操作失败");
            },1000);
        });
        return p1;
    }

    // runAsync2() 函数
    function runAsync2(){
        var p2 = new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log("异步任务2执行完成");
                resolve("操作成功2");
                reject("操作失败");
            },3000);
        });
        return p2;
    }

    // runAsync3() 函数
    function runAsync3(){
        var p3 = new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log("异步任务3执行完成");
                resolve("操作成功3");
                reject("操作失败");
            },3000);
        });
        return p3;
    }

  // all  谁最慢,以谁为准执行回调
    /*
    * 输出     异步任务1执行完成  异步任务2执行完成   异步任务3执行完成
    *         (3) ["操作成功1", "操作成功2", "操作成功3"]
    */
    // promise.all接收一个数组参数,三个异步操作并行执行
    // 在它们都执行完以后进入到then里面,all会把所有异步操作的返回结果数据放进一个数组中传给then
    Promise.all([runAsync1(),runAsync2(),runAsync3()]).then(function(result){
         console.log(result);
    });

race的用法
1)语法:Promise.race(iterable)
2)该方法同样是将多个Promise实例,包装成一个新的Promise实例
3)Promise.race方法同样接受一个数组(或具有Iterator接口)作参数。当p1, p2, p3中有一个实例的状态发生改变(变为fulfilled或rejected),p的状态就跟着改变。并把第一个改变状态的promise的返回值,传给p的回调函数
4)并行执行异步操作的能力,与all类似,不同的是谁最快,以谁为准执行回调
5)实例如下:

function runAsync1(){
        var p1 = new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log("异步任务1执行完成");
                resolve("操作成功1");
                reject("操作失败");
            },1000);
        });
        return p1;
    }

    // runAsync2() 函数
    function runAsync2(){
        var p2 = new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log("异步任务2执行完成");
                resolve("操作成功2");
                reject("操作失败");
            },3000);
        });
        return p2;
    }

    // runAsync3() 函数
    function runAsync3(){
        var p3 = new Promise(function(resolve,reject){
            setTimeout(function(){
                console.log("异步任务3执行完成");
                resolve("操作成功3");
                reject("操作失败");
            },3000);
        });
        return p3;
    }

   /*
    * 输出    异步任务1执行完成  操作成功1  异步任务2执行完成  异步任务3执行完成
    */
    Promise.race([runAsync1(),runAsync2(),runAsync3()]).then(function(result){
        console.log(result);
    });

6) race的应用场景,给某个异步请求设置超时时间,并且在超时后执行相应的操作
实例如下:

// 请求某个图片资源
   function requestImg(){
       var p4 = new Promise(function(resolve,reject){
           var img = new Image();
           img.onload = function(){
               resolve(img);
           };
           img.src = "xxxxxx";
       });
       return p4;
   }

   //  延时函数,用于给请求计时
   function timeout(){
       var p5 = new Promise(function(resolve,reject){
           setTimeout(function(){
              reject("请求超时,响应失败");
           },6000);
       });
       return p5;
   }

   /*
   * 输出    Failed to load resource: the server responded with a status of 404 (Not Found)
   *         请求超时,响应失败
   */
   //将 requestImg()和timeout()函数并且执行异步操作,timeout()函数是一个延时6s的异步操作
   // 将它们返回Promise对象的函数放进race,就会进行比赛,看谁会先
   // 如果6s内图片资源请求成功,那么就进入到then方法中,如果6s内图片资源请求失败,被catch捕获报错
   Promise.race([requestImg(),timeout()]).then(function(result1){
       console.log(result1);
   }).catch(function(result2){
       console.log(result2);
   });

热门文章

暂无图片
编程学习 ·

DAY3-JDBC+Tomcat实现简易登陆系统

目录笔记bean包下User类dao包下UserDao类service包下UserServiceImpl类和UserService接口servlet包下HelloServlet类和LoginServlet类util包下DBUtil类xml文件 笔记 jdbc: 1.加载驱动 2.创建连接 3.写sq| 4.得到statement对象 5.执行sq|得到结果集 6.处理结果集 7.关闭资源 分…
暂无图片
编程学习 ·

Shell编程_echo/printf

目录一、Shell echo/printf 命令1、Shell显示命令-echo2、printf 命令操作常用的一些格式化字符二、test命令一、Shell echo/printf 命令Shell echo/printf 命令1、Shell显示命令-echo打印普通字符串[root@master ~]# echo "hello shell" hello shell创建和清空文件1…
暂无图片
编程学习 ·

命令模式

菜鸟教程中代理模式总结)1.定义:将请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令 的合适的对象,并把该命令传给相应的对象,该对象执行命令。 2.主要解决:行为请求者与行为实现者通常是一种紧耦合的关系,为了消除这种耦合关系 3.何时使用:命…
暂无图片
编程学习 ·

Python入门的学习心得

由于我是自学Python,非科班出生,所以只能分享一些关于我的学习心得,如果有不对地方欢迎指正。不过非科班出生虽然是一个痛点,但是在工作上,我其实不输给我其他同事,这点我倒是很有自信,而且我也统一一句话“目前互联网上的免费编程课程,足够让你成为一个合格的码农”。…
暂无图片
编程学习 ·

JDBC

JDBC(Java DataBase Connectivity) JDBC 简介 Java 数据库连接技术。即用 Java 程序操作数据库的一套接口。是独立与 特定数据库(MySQL、SQLServer) 的管理系统,也就是无论使用的是什么类型的数据库都可以用 JDBC 去连接。 让 JDBC 去翻译底层数据库的各种指令,我们只需要使…
暂无图片
编程学习 ·

nat表中的dnat snat的使用(iptables)

nat表中的dnat snat的使用- snatiptables -t nat -A POSTROUTING -o enp6s0 -j SNAT --to-source 172.25.254.33- dnatiptables -t nat -A PREROUTING -i enp6s0 -j DNAT --to-dest 170.25.254.22从enp6s0进来的所有数据都转给170.25.254.11,即给enp6s0(172.25.254.33)的所有…
暂无图片
编程学习 ·

java 并发 join 之 老王泡茶

package juc;import java.util.concurrent.TimeUnit;/*** @author yanjun.liu* @date 2020/7/1--17:00*/ public class Test6 {public static void main(String[] args) throws InterruptedException {Thread lw= new Thread(()->{try {System.out.println("老王开始洗…
暂无图片
编程学习 ·

SpringBoot切换不同的实现时,出现nullPointer问题

1,有时候算法需要迭代,相同的接口需要多个实现,自己可以随意切换,接口类:package com.xxx.shortvideo.manager;public interface VideoRecommendedManger {FeedVideoByUserDTO getVideoByUser(FeedVideoByUserReq req);}2, 针对接口有两个实现3,通过配置config类来实现pa…
暂无图片
编程学习 ·

Tensorflow实现卷积神经网络

Tensorflow实现卷积神经网络Tensorflow实现卷积神经网络卷积层池化层归一化层实现简单的卷积神经网络 Tensorflow实现卷积神经网络 卷积层 卷积核,步福,填充,多通道卷积,激活函数,卷积函数。 主要函数使用: 1.conv2d函数 tf.nn.conv2d(input, filter, strides, padding, …
暂无图片
编程学习 ·

ubuntu如何远程连接和控制RS的仪表

RS有一台矢量信号源仪表 SMBV100B。需要通过安装了ubuntu的pc机局域网传matlab波形文件并设置频点等。需要3个步骤:1,安装ArbToolbox,用于将matlab波形转换成RS信号源识别的波形文件;https://www.rohde-schwarz.com.cn/applications/r-s-arb-toolbox-application-note_5628…
暂无图片
编程学习 ·

Kotlin上手(一)

标准函数with with函数接收两个参数,第一个参数是任意类型的对象,第二个是Lambda表达式。with函数会在Lambda表达式中提供第一个参数的上下文,并使用Lambda表达式中的最后一行代码作为返回值返回。 fun test() {val list = listOf("Apple", "Banana", &…
暂无图片
编程学习 ·

基于JavaWeb的宿舍管理系统(源码+数据库)无论文

背景: 管理信息系统在现代社会已深入到各行各业,由于计算机技术的迅速发展和 普及,信息管理系统MIS事实上已成为计算机管理信息系统,大学生宿舍管理系统就是一个典型的管理信息系统,它可以让宿舍管理工作变的更轻松。 任务:设计一个大学生宿舍管理系统。 目的: 实现系统维护…
暂无图片
编程学习 ·

tensorflow-serving布置facenet心得

这个的东西困扰我很久,终于弄成了。不知道我做的是不是太繁琐,如果有人做的更简单,希望指出,谢谢。docker中,做了两个容器,一个放的mtcnn,一个放的facent。他们并不是多模型布置的。mtcnn其中包括:pnet,rnet和onet,这三个是多模型布置。客户端通过调用mtcnn,得到返回…
暂无图片
编程学习 ·

Unity通过代码实现预制体Apply保存

if (PrefabUtility.GetPrefabParent(weaponPanel.gameObject) != null) {PrefabUtility.ReplacePrefab(weaponPanel.gameObject,PrefabUtility.GetPrefabParent(weaponPanel), ReplacePrefabOptions.ConnectToPrefab); }
暂无图片
编程学习 ·

《Webpack》

以下内容纯属个人扯淡,仅供参考1、概述1。当前Web开发的问题文件依赖关系错综复杂 静态资源请求效率低 模块化支持不友好 浏览器对高级JS特性兼容程度较低 etc...而Webpack的引入,提供友好的模块化支持,代码压缩混淆,处理js兼容问题,性能优化等,提高可维护性,它是一个流…
暂无图片
编程学习 ·

VUE怎么动态显示本地文件夹中的图片

需求描述:VUE动态显示本地某一文件夹中的图片 如果我们在项目目录中有一个文件夹,文件夹中有一些图片,我们不知道图片的名字,只知道图片存放的目录,我们想把这些图片展示在VUE页面上,那么我们应该如何实现这个功能呢? 解决思路: 首先我们需要知道图片的路径,图片的路径…
暂无图片
编程学习 ·

指针的偏移

基本数据类型指针偏移//方便演示,先初始化一个指针为空。 int* a = nullptr; a = a+1;//+1就偏移一个单位,而a定义的是指向int型数据的指针,int型有4个字节,所以输出就是00000004//假设我们定义一个一维数组int arr[5]={1,2,3,4,5};int* p= &arr[0];//这里也可以直接写…
暂无图片
编程学习 ·

学习笔记

关于Java基础 为什么String定义为final 让String不可变不可变就是第二次给一个String 变量赋值的时候,不是在原内存地址上修改数据,而是重新指向一个新对象,新地址。这样做主要是为了“安全性”和“效率” 若 String允许被继承, 由于它的高度被使用率, 可能会降低程序的性能…