• 前端异步代码烧脑的原因


    同步与异步

    同步:等待结果
    异步:不等待结果

    注意,异步常常伴随回调一起出现,但是异步不是回调,回调也不一定是异步

    如下代码就是同步,只要fn内部的循环完毕才能执行下面的代码

    function fn(){
        for(var i=0;i<5000000000;i++){
            
        }
    }
    var startTime = new Date().getTime();
    console.log('开始了')
    fn();
    var exeTime = new Date().getTime() - startTime;
    console.log('执行了'+exeTime/1000+'秒')
    

      

    异步代码

    • 定时器就相当于浏览器设置了一个闹钟
    • 之后继续执行
    • 闹钟到了时间再去执行相应的代码
    //异步的 asyncFn
    function asyncFn(fn){
        setTimeout(fn, 3000)
    }
    console.log(1)
    asyncFn(()=> console.log('wake up'))
    console.log(2)

    前端经常遇到的异步

    //因为图片还没有加载完毕所以并不知道高度,(请禁用浏览器缓存在执行)
    document.getElementsByTagNames('img')[0].width // 宽度为 0
    console.log('done')

    你应该在图片加载成功后在获取图片的高度

    document.getElementsByTagNames('img')[0].onload = function(){
        console.log(this.width) // 宽度不为 0
        console.log('real done')
    }
    console.log('done')

    面试题中的异步

    为啥点击li每次都打印 6

    <li>11111111</li>
    <li>22222222</li>
    <li>33333333</li>
    <li>44444444</li>
    <li>55555555</li>
    <li>66666666</li>
    
    ......
    
    let liList = document.querySelectorAll('li')
    for(var i=0; i<liList.length; i++){
        liList[i].onclick = function(){
            console.log(i)
        }
    }
    console.log(i)  //6
    1. for循环用极短的时间执行完毕 我们假设 3 毫秒
    2. var i 有变量提升 即使循环之后仍然可以访问到i的值
    3. 用户看到 Li 的时间绝对大于3 毫秒,每个 Li 点击事件已经初始化结束
    4. log(i) 还是外面的 i

    如何解决 把 var i 改为 let i 就可以了

    详情参考

    AJAX 中的异步

    这样调用虽然是同步的,但是页面会失去响应,直到结果回来

    let request = $.ajax({
      url: '.',
      async: false
    })
    console.log(request.responseText)
    //异步方式
    $.ajax({
        url: '/',
        async: true,
        success: function(responseText){
            console.log(responseText)
        }
    })
    

      

    异步的形式

    一般有两种方式拿到异步结果

    傻逼方法:轮训
    老板叫你去买土豆,然后他就隔1分钟问一次买到土豆了吗?

    正规方法:回调
    老板叫你去买土豆并告诉你回来之后通知他,20分钟后你回来了告诉老板是否买到了土豆

    回调的形式

    Node.js 的 error-first 形式

    fs.readFile('./1.txt', (error, content)=>{
        if(error){
            // 失败
        }else{
            // 成功
        }
    })

    jQuery 的 success / error 形式

    $.ajax({
        url:'/xxx',
        success:()=>{},
        error: ()=>{}
    })

    jQuery 的 done / fail / always 形式

    $.ajax({
        url:'/xxx',
    }).done( ()=>{} ).fail( ()=>{} ).always( ()=> {})

    Prosmise 的 then 形式

    //成功调第一个函数,失败调用第二个函数
    $.ajax({
        url:'/xxx',
    }).then( ()=>{}, ()=>{} ).then( ()=>{})
    • ajax的 Prosmise跟真正的Promise有点不同
    • 它也可以一直.then().then().then() 这样链式操作

    Promise只是一种回调的形式

    Promise规范

    axios({
        url:'xxx'
    }).then(s1,e1)
      .then(s2,e2)
      .then(s3,e3)

    如果是jquery,成功只会走进成功的分支

    s1 ==> s2 ==> s3 
    e1 ==> e2 ==> e3
    

    Promise规范是责任制

    s1 e1 是第一责任人
    s2 e2 是第二责任人
    s3 e3 是第三责任人
    axios({
        url:'/aaa'
    }).then(()=>{
        console.log('成功1')
        alert(xxxxx)  //xxxxx 没有定义
    },()=>{
        console.log('失败1')        
    }).then(()=>{
        console.log('成功2')
    },()=>{
        console.log('失败2')
    })
    1. s1 执行过程不够顺利报错了(就会产生错误的结果)
    2. 于是第二责任人接收到的是一个烂尾工程
    3. 当做错误处理 走进e2

    如果没有 e2 就会报错到浏览器显示给开发者

    两种写法
    axios({
        url:'/aaa'
    }).then(()=>{
        console.log('成功1')
        alert(xxxxx)  //xxxxx 没有定义
    },()=>{
        console.log('失败1')        
    }).then(()=>{
        console.log('成功2')
    }).catch((err)=>{
        console.log(err)
    })
    
    
    axios({
        url:'/aaa'
    }).then(()=>{
        console.log('成功1')
        alert(xxxxx)  //xxxxx 没有定义
    },()=>{
        console.log('失败1')        
    }).then(()=>{
        console.log('成功2')
    }).then(undefined,(err)=>{
        console.log(err)
    })

    自己返回 Promise

    function buyApple(){
        var fn =(succ,err)=>{
            var rnd = Math.random()
            setTimeout(()=>{
                if(rnd>0.5){
                    succ('买到了苹果')
                }else{
                    err('没买到苹果')
                }
            },2000)
        }
        return new Promise(fn)   //fn.call(undefined,success,err)
    }
    
    
    var promise = buyApple();
    promise.then(
        (data)=>{console.log(data)},
        (err)=>{console.log(err)}
    )
    function ajax(){
        return new Promise((resolve, reject)=>{
            做事
            如果成功就调用 resolve
            如果失败就调用 reject
        })
    }
    
    var promise = ajax()
    promise.then(successFn, errorFn)

    Promise 深入阅读:http://www.cnblogs.com/hustskyking/p/promise.html
    Promise/A+ 规范:https://segmentfault.com/a/1190000002452115

    async / await

    await
    function buyApple(){
        var fn =(succ,err)=>{
            var rnd = Math.random()
            setTimeout(()=>{
                if(rnd>0.5){
                    succ('买到了苹果')
                }else{
                    err('没买到苹果')
                }
            },10000)
        }
        return new Promise(fn)   //fn.call(undefined,success,err)
    }
    
    var result = await buyApple();
    console.log(2)

    10秒之后返回买苹果的结果(成功/失败)
    打印2

    • await相当于 用异步的方式写同步的代码
    • 直到异步代码结果回来才把值赋给 result

    await的用法就是 后面接一个返回Promise对象的函数

    async
    function buyApple(){
        var fn =(succ,err)=>{
            var rnd = Math.random()
            setTimeout(()=>{
                if(rnd>0.5){
                    succ('买到了苹果')
                }else{
                    err('没买到苹果')
                }
            },10000)
        }
        return new Promise(fn)   //fn.call(undefined,success,err)
    }
    
    async function fn(){
        var result = await buyApple()
        return result
    }
    
    var r = await fn().then(
                        (data)=>{console.log(data)},
                        (err)=>{console.log(err)})
    console.log(1)

    分析1

    var r = await fn().then(
                        (data)=>{console.log(data)},
                        (err)=>{console.log(err)})
    console.log(2)
    /*
    1.会直接打印2
    2. 10秒后打印  买苹果的结果
    */

    分析2

    var r = await fn().then(
                        (data)=>{console.log(data)},
                        (err)=>{console.log(err)})
    console.log(2)
    /*
    10秒后打印  买苹果的结果
    然后打印2
    */
  • 相关阅读:
    常用正则表达大全
    Vue 中Bus使用
    js 获取图片url的Blob值并预览
    npm install 插件 --save与 --save -dev的区别
    layui 事件监听触发
    微信小程序之回到顶部的两种方式
    HTML 命名规范!
    SpringMvc 集成支付宝沙盒测试
    Linux 的 tomcat 自起服务
    Linux 安装 tomcat8 详细步骤
  • 原文地址:https://www.cnblogs.com/yangsg/p/10150638.html
Copyright © 2020-2023  润新知