• JavaScript异步和单线程


    一,同步和异步的区别:
    同步会阻塞代码执行,而异步不会。(比如alert是同步,setTimeout是异步)
    二,前端使用异步的场景:
    1,定时任务:setTimeout,setInterval
    2,网络请求:ajax请求,动态<img>加载
    3,事件绑定
    三,什么是单线程,和异步有什么关系
    1,什么是单线程:只有一个线程,同一时间只能做一件事情,从上到下执行
    例:
        // 循环执行期间,JS 执行和DOM渲染暂时卡顿
        var i, sum = 0;
        for(var i = 0; i < 1000000000; i++) {
            sum += 1;
        }
        console.log(sum);
    
        // alert 不处理,JS 执行和DOM渲染暂时卡顿
        alert('hello');
        console.log(2);
    2,为什么是单线程:避免DOM渲染的冲突:
    (1),浏览器需要渲染DOM
    (2),JS可以修改DOM结构
    (3),JS执行的时候,浏览器DOM渲染会暂停
    (4),两段JS也不能同时执行
    (5),为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质
     
    3,JS单线程解决方案:异步
        console.log(100)
        setTimeout(function() {
            console.log(200)   //反正1000ms 之后执行
        },1000)                //先不管它,先让其他JS代码执行
        console.log(300);
    var ajax = $.ajax({
            url: './data.json',
            success: function(result) {  //ajax加载完才执行
                console.log(result);     //先不管它,先让其他JS代码执行
            }
        })
        console.log(ajax) //{readyState: 1, getResponseHeader: ƒ, getAllResponseHeaders: ƒ, setRequestHeader: ƒ, overrideMimeType: ƒ, …}
        console.log(200); //200 
    1,异步的问题:
    问题一,没按照书写方式顺序执行,可读性差
    问题二,callback中不容易模块化
    四,什么是event-loop(异步方案的实现方式)
    event-loop:事件轮询,JS实现异步的具体解决方案。
    -同步代码,直接执行
    -异步函数先放在 异步队列 中
    -待同步函数执行完毕,轮询执行 异步队列 中的函数
    (定时任务,网络请求,事件绑定)
    例1:
    例2:
    例3:
    五,jquery的Deferred
    1, jQuery1.5的变化:使用jQuery Deferred,初步引入Promise概念。
    (1)仍不会也无法改变js异步和单线程的本质
    (2)只能从写法上杜绝callback这种形式
    (3)它是一种语法糖形式,但是解耦了代码
    (4)很好地体现:开放封闭原则(对扩展开放,对修改封闭)(多人开发协作不相互干扰,减少回归测试的成本)
        // jQuery1.5之前
        var ajax = $.ajax({
            url: './data.json',
            success: function(result) {
                console.log('success1');
                console.log('success2');
                console.log('success3');
            },
            error:function(){
                console.log('error');
            }
        })
        console.log(ajax) //返回一个XHR对象
        // jquery1.5之后
        var ajax = $.ajax('./data.json');
        ajax.done(function() {
                console.log('success1');
            })
            .fail(function() {
                console.log('error1');
            })
            .done(function() {
                console.log('success2')
            })
        console.log(ajax); //返回一个deferred对象,可以进行链式操作
    
        // 还可以使用很像promise写法
        var ajax = $.ajax('./data.json');
        ajax.then(function(){
            console.log('success1');
        },function(){
            console.log('error1');
        }).then(function(){
            console.log('success2');
        },function(){
            console.log('error2');
        })

    2,jQuery Deferred应用

    看一个简单的例子:

       // 给出一段非常简单的异步操作代码,使用setTimeout函数
        var wait = function() {
            var task = function() {
                console.log('执行完成');
                // 1
                // 2
                // 3
            }
            setTimeout(task, 1000)
        }
        wait()
        // 新增需求:要在执行完成之后进行某些特别复杂的操作,代码可能会很多,而且分好几个步骤

    使用Deferred:(开放封闭原则)

       function waitHandler() {
            // 定义
            var dtd = $.Deferred(); //创建一个deferred对象
            var wait = function(dtd) { //要求传入一个deferred对象
                var task = function() {
                    console.log('执行完成');
                    // 成功
                    dtd.resolve(); //表示异步任务已经完成
                    // 失败
                    // dtd.reject();  //表示异步任务失败或出错
                }
                setTimeout(task, 1000)
                // wait返回
                return dtd; //要求返回deferred对象
            }
            // 最终返回  注意这里一定要有返回值
            return wait(dtd);
        }
    
        var w = waitHandler();
        w.then(function() {
            console.log('ok1');
        }, function() {
            console.log('err1');
        }).then(function() {
            console.log('ok2');
        }, function() {
            console.log('err2');
        })
    
        // 还有 w.done  w.fail

    执行reject

      function waitHandler() {
            // 定义
            var dtd = $.Deferred(); //创建一个deferred对象
            var wait = function(dtd) { //要求传入一个deferred对象
                var task = function() {
                    console.log('执行完成');
                    // 成功
                    // dtd.resolve(); //表示异步任务已经完成
                    // 失败
                    dtd.reject(); //表示异步任务失败或出错
                }
                setTimeout(task, 1000)
                // wait返回
                return dtd; //要求返回deferred对象
            }
            // 最终返回  注意这里一定要有返回值
            return wait(dtd);
        }
    
        var w = waitHandler();
        w.then(function() {
            console.log('ok1');
        }, function() {
            console.log('err1');
        })
        w.then(function() { //reject需要分开,不然执行顺序就错啦
            console.log('ok2');
        }, function() {
            console.log('err2');
        }) 

    这里注意一个原则:开放封闭原则

    总结,dtd的API可分成两类,用意不同

    第一类(主动执行):dtd.resolve dtd.reject
    第二类(监听):dtd.then dtd.done dtd.fail
    这两类应该分开,否则后果很严重
    可在上面代码最后执行dtd.reject()试下结果

    使用dtd.promise()

      function waitHandler(){
            var dtd = $.Deferred();//Deferred 
            var wait = function(dtd) {
                var task = function(){
                    console.log('执行完成');
                    dtd.resolve();
                }
                setTimeout(task,2000);
                return dtd.promise();//注意,这里返回的是promise,而不是直接返回deferred对象
            }
            return wait(dtd);
        }
        var w = waitHandler();//经过上面的改动,w接收的就是一个promise对象
        $.when(w)
        .then(function(){
            console.log('ok1');
        })
        .then(function(){
            console.log('ok1');
        })
        w.reject()//执行这句会报错 w.reject is not a function  promise不能使用,只有监听的方法了,使用者只能监听,开发人员封装的时候才能用

     说明promise和deferred区别: promise只能被动监听,不能主动执行

    六,Promise的基本使用和原理

    1,基本语法回顾(备注:现在高级浏览器基本都支持promise,如果有些不支持,可以在cdn上找,引入 bluebird )

    <script src="https://cdn.bootcss.com/bluebird/3.5.1/bluebird.min.js"></script>
       function loadImg(src) {
            const promise = new Promise(function(resolve, reject) { //new Promise实例
                var img = document.createElement('img');
                img.src = src;
    
                img.onload = function() {
                    resolve(img);
                }
                img.onerror = function() {
                    reject('图片加载失败');
                }
            });
            return promise; //返回 Promise实例
        }
        var src = "https://shared-https.ydstatic.com/dict/v2016/result/logo.png";
        var result = loadImg(src); //Promise实例
        result.then(function(img) { //then监听结果,成功时执行resolve(), 失败时执行reject()
            console.log(1, img.width); //1 164
            return img;
        }, function(img) {
            console.log('failed');
            return img;
        }).then(function(img) {
            console.log(2, img.height) //2 36
        })

    2,异常捕获

        // 规定:then只接受一个参数(成功的处理函数),最后统一用catch捕获异常
        // var src = "https://shared-https.ydstatic.com/dict/v2016/result/logo.png";
        var src = "https://shared-https.ydstatic.com/dict/v2016/result/logo_1.png";//写一个不存在的地址
        var result = loadImg(src);
        result.then(function(img) {
            console.log(img.width);
            return img;
        }).then(function(img) {
            console.log(img.height);
        }).catch(function(ex) {
            // 最后统一用catch捕获异常
            console.log(ex); //图片加载失败  (logo_1.png:1 GET https://shared-https.ydstatic.com/dict/v2016/result/logo_1.png 404 (Not Found))
        })

    自定义错误:

    function loadImg(src) {
            const promise = new Promise(function(resolve, reject) { //new Promise实例
                var img = document.createElement('img');
                // 模拟抛出错误 模拟语法错误,逻辑之外的bug
                throw new Error('自定义错误');
                img.src = src;
    
                img.onload = function() {
                    resolve(img);
                }
                img.onerror = function() {
                    reject('图片加载失败');
                }
            });
            return promise; //返回 Promise实例
        }
    
         // 规定:then只接受一个参数(成功的处理函数),最后统一用catch捕获异常
        var src = "https://shared-https.ydstatic.com/dict/v2016/result/logo.png";
        // var src = "https://shared-https.ydstatic.com/dict/v2016/result/logo_1.png"; //写一个不存在的地址
        var result = loadImg(src);
        result.then(function(img) {
            console.log(img.width);
            return img;
        }).then(function(img) {
            console.log(img.height);
        }).catch(function(ex) {
            // 最后统一用catch捕获异常
            console.log(ex); 
        })

    3,多个串联

       // 多个串联 图片只是用来模拟,实际可能更多的不是用图片,比如处理用户信息啥的,加载用户信息的时候,先拿到用户信息,再去处理其他信息等
        var src1 = 'https://shared-https.ydstatic.com/dict/v2016/result/logo.png';
        var result1 = loadImg(src1);
        var src2 = 'https://img.mukewang.com/user/57b98e990001351004400440-100-100.jpg';
        var result2 = loadImg(src2);
    
        // 链式操作
        result1.then(function(img1) {
            console.log('第一个图片加载完成', img1.width);
            return result2; //重要!!!
        }).then(function(img2) {
            console.log('第二个图片加载完成', img2.width);
        }).catch(function(ex) {
            // 最后统一用catch捕获异常
            console.log(ex);
        })

    4,Promise.all (全部promise实例完成后) 和 Promise.race (只要有一个promise实例完成)

       // Promise.all 接收一个包含多个promise实例的数组,待全部完成之后,统一执行success
        var src1 = 'https://shared-https.ydstatic.com/dict/v2016/result/logo.png';
        var result1 = loadImg(src1); //pending
        var src2 = 'https://img.mukewang.com/user/57b98e990001351004400440-100-100.jpg';
        var result2 = loadImg(src2);
        Promise.all([result1, result2]).then(datas => {
            // 接收到的datas是一个数组,依次包含了多个promise返回的内容
            console.log('all', datas[0]); //all <img src=​"https:​/​/​shared-https.ydstatic.com/​dict/​v2016/​result/​logo.png">​
            console.log('all', datas[1]); //all <img src=​"https:​/​/​img.mukewang.com/​user/​57b98e990001351004400440-100-100.jpg">​
        })

    Promise.race (只要有一个promise实例完成)

        // Promise.race 接收一个包含多个promise实例的数组
        // 只要有一个完成,就执行success
        Promise.race([result1, result2]).then(data => {
            // data即最先执行完成的promise的返回值
            console.log('race', data); //race <img src=​"https:​/​/​shared-https.ydstatic.com/​dict/​v2016/​result/​logo.png">​
        })

    promise标准

    1,关于“标准”的闲谈

    任何技术推广使用都需要一套标准来支撑 
    如html css js等,无规矩不成方圆
    任何不符合标准的东西,终将会被用户抛弃
    不要挑战标准,不要自造标准

    2,状态变化

    三种状态:pending fulfilled rejected
    pending:   初始状态
    fulfilled: 成功
    rejected:失败

    pending到fulfilled,或者pending到rejected,状态不可逆

    3,then

    Promise实例必须实现then方法
    then()必须可以接收两个函数作为参数
    then()返回的必须是一个Promise实例
    如果then中没有返回promise实例,则默认返回的是上一个promise实例,如果返回了一个promise实例,那就默认为返回的promise实例

    七,async/await(和promise区别、联系等)(babel已经支持,koa也已经支持)

    1,then只是将callback拆分了

        // then只是将callback拆分了
        var w = waitHandler();
        w.then(function() {
            console.log('ok1');
        }, function() {
            console.log('err1');
        }).then(function() { 
            console.log('ok2');
        }, function() {
            console.log('err2');
        })


    2,async/await是最直接的同步写法

       // async/await是最直接的同步写法
        const load = async function() {
            const result1 = await loadImg(src1);
            console.log(result1);
            const result2 = await loadImg(src2);
            console.log(result2);
        }
        load()

    完整的:

        import 'babel-polyfill'; //引入babel-polyfill 
    
        // async/await是最直接的同步写法
        function loadImg(src) {
            const promise = new Promise(function(resolve, reject) {
                var img = document.createElement('img');
                img.src = src;
    
                img.onload = function() {
                    resolve(img);
                }
                img.onerror = function() {
                    reject('图片加载失败');
                }
            });
            return promise;
        }
        var src1 = 'https://shared-https.ydstatic.com/dict/v2016/result/logo.png';
        var src2 = 'https://img.mukewang.com/user/57b98e990001351004400440-100-100.jpg';
        // async/await是最直接的同步写法
        const load = async function() { //函数必须用async标识
            const result1 = await loadImg(src1); //await后面跟的是一个Promise实例
            console.log(result1);
            const result2 = await loadImg(src2); //await后面跟的是一个Promise实例
            console.log(result2);
        }
        load()

    用法:
    要在函数体内使用await,函数必须用async标识
    await后面跟的是一个Promise实例
    需要安装babel-polyfill

    npm i --save-dev babel-polyfill 

    3,总结

    使用了Promise,并没有和Promise冲突
    完全是同步的写法,再也没有回调函数
    但是:改变不了js单线程,异步的本质

     
    八,当前 JS 解决异步的方案总结

    1,jquery Deferred
    2,Promise
    3,Async/await

  • 相关阅读:
    spring mvc controller间跳转 重定向 传参
    SpringMVC拦截器(资源和权限管理)
    Spring3 MVC 拦截器拦截不到的问题
    使用HandlerInterceptor实现简单的授权
    同一个form里,不管哪个 submit 都是直接提交form表单里的内容
    AJax+springMVC+JQURY.GET--注册界面即时刷新用户名是否存在
    Ajax异步检查用户名是否存在(附Demo下载)
    Ajax注册表单用户名实时验证
    SpringMVC记住密码功能(实例)
    CocoaPods停在Analyzing dependencies解决方案
  • 原文地址:https://www.cnblogs.com/iceflorence/p/8903913.html
Copyright © 2020-2023  润新知