js的异步

一、单线程
单线程:在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。

为什么JS采用单线程?

  • 浏览器需要渲染DOM
  • JS可以修改DOM
  • JS执行的时候浏览器DOM渲染停止
  • 两段JS不能同时执行(同时渲染DOM就冲突了)
  • webworker支持多线程,但是不能访问DOM

为了避免渲染DOM冲突,JS必须是单线程的,而且和浏览器渲染共用一个线程。

二、异步
单线程的解决方案就是异步。

异步有哪些问题?

  • 问题一、没按照书写的顺序执行,可读性差。
  • 问题二、callback中不容易模块化。

三、event-loop
  人们把javascript调控同步和异步任务的机制称为事件循环(event-loop)
event-loop机制如下:
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

  • 所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
  • 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
  • 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件,哪些对应的异步任务,是结束等待状态,进入执行栈,开始执行。
  • 主线程不断重复上面的第三步
        setTimeout(function () {
            console.log(1)
        },1000)
        setTimeout(function(){
            console.log(2)
        })

        console.log(3)

        //3  2  2

四、jQuery的Deferred
1、jquery 1.5之前Ajax的写法

var ajax = $.ajax({
            utl:'data.json',
            success:function(){
                console.log('success1')
                console.log('success2')
                console.log('success3')
            },
            error:function(){
                console.log('error')
            }
        })
console.log(ajax) //返回一个XHR对象

2、jquery 1.5之后Ajax的写法
第一种写法

var ajax = $.ajax('data.json')
        ajax.done(function(){
            console.log('success1')
        })
            .fail(function(){
                console.log('error')
            })
            .done(function(){
                console.log('success2')
            })
        console.log(ajax) //返回一个deferred对象

第二种写法

var ajax = $.ajax('data.json')
        ajax.then(function(){
            console.log('success1')
        },function(){
            console.log('error1')
        }).then(function(){
            console.log('success2')
        },function(){
            console.log('success2')
        })

这种写法很像Promise的写法
3、使用jquery Deferred

        function waitHandle(){
           var dtd = $.Deferred()

           var wait = function(dtd){
               var task = function (){
                   console.log('执行完成')
                   //执行成功
                   dtd.resolve()

                   //执行失败
                  // dtd.reject()
               }
               setTimeout(task,2000)

               return dtd
           }

           return wait(dtd)
        }

        var w = waitHandle()
        w.then(function (){
            console.log('OK 1')
        },function (){
            console.log('err 1')
        })
        w.then(function (){
            console.log('OK 2')
        },function (){
            console.log('err 2')
        })
        w.then(function (){
            console.log('OK 3')
        },function (){
            console.log('err 3')
        })

        //开放封闭原则:对扩展开放,对修改封闭

  • 总结,dtd的api可分为两类,用意不同。
  • 第一类:dtd.resolve dtd.reject
  • 第二类:dtd.then dtd.done dtd.fail
  • 这两类应该分开,否则后果很严重!
  • 可以在上面代码最后执行dtd.reject()试一下后果

使用dtd.promise()

      function waitHandle(){
           var dtd = $.Deferred()

           var wait = function(dtd){
               var task = function (){
                   console.log('执行完成')
                   //执行成功
                   dtd.resolve()

                   //执行失败
                  // dtd.reject()
               }
               setTimeout(task,2000)

               return dtd.promise()
           }

           return wait(dtd)
        }

        var w = waitHandle() //promise对象
        $.when(w).then(function (){
            console.log('OK 1')
        },function (){
            console.log('err 1')
        })
        $.when(w).then(function (){
            console.log('OK 2')
        },function (){
            console.log('err 2')
        })
        $.when(w).then(function (){
            console.log('OK 3')
        },function (){
            console.log('err 3')
        })

        //开放封闭原则:对扩展开放,对修改封闭

五、Promise的基本使用和原理

        function loadImg(src) {
            var promise = new Promise(function(resolve, reject){
                var img = document.createElement('img')
                //throw new Error('自定义错误')
                img.onload = function(){
                    resolve(img)
                }
                img.onerror = function(){
                    reject('图片加载失败!')
                }
                img.src = src
            })
            return promise
        }

        var src = "https://img10.360buyimg.com/n1/jfs/t3847/338/2200026645/140646/c239125d/58537d88Nc15f021d.jpg"
        var result = loadImg(src)
        result.then(function(img){
            console.log(1,img.width)
            return img
        }).then(function (img) {
            console.log(2,img.height)
        }).catch(function (ex){
            console.log(ex)
        })

多个串联
Promise.all()等所有都执行完后,执行then中内容。
Promise.race()有一个执行完就执行then中内容。

       function loadImg(src) {
            var promise = new Promise(function(resolve, reject){
                var img = document.createElement('img')
                //throw new Error('自定义错误')
                img.onload = function(){
                    resolve(img)
                }
                img.onerror = function(){
                    reject('图片加载失败!')
                }
                img.src = src
            })
            return promise
        }

        var src2 = 'https://img11.360buyimg.com/n1/s450x450_jfs/t3427/221/1828863550/93027/757e93ac/5832861fN2566f854.jpg'
        var result1 = loadImg(src2)
        var src1 = 'https://img10.360buyimg.com/mobilecms/s250x250_jfs/t22588/177/2455233485/268618/d7383c4f/5b83afceNdbaff291.jpg'
        var result2 = loadImg(src1)
        Promise.all([result2,result1]).then(function (datas){
            console.log('all',datas[0])
            console.log('all',datas[1])
        })

        Promise.race([result1,result2]).then(function (data){
            console.log('race',data)
        })

Promise的标准-状态变化

  • 三种状态:pending fulfilled rejected
  • 初始状态是pending
  • pending可变为fulfilled或rejected
  • 状态变化不可逆

Promise的标准-then

  • Promise实例必须实现then这个方法
  • then必须接收两个或一个函数作为参数
  • then返回的必须是一个Promise实例(如果没有明确返回,那么它返回的是本身)

Promise的原理
  Promise其实内部也有一个defers队列存放事件,.then的事件就在里面,程序开始执行的时候,.then就已经放入下一个事件,然后后面当异步操作完成时,resolve触发事件队列中的事件,便完成了一个.then操作, 其实到这里我们就可以很快地想出一种解决方案,每次异步操作完成通过resolve触发事件并将事件从事件队列中移除,通过事件队列中的事件的resolve使事件的触发持续下去。

我们可以用十几行代码就可以实现这样的逻辑,实现一个简单的异步编程方案

function P(fn) {
 var value = null;
 var events = [];
 this.then = function(f) {
   events.push(f);
   return this;
 }
 function resolve(newValue) {
   var f = events.shift();
   f(newValue, resolve);
 }
 fn(resolve);
}

function a() {
 return new P(function(resolve) {
   console.log("get...");
   setTimeout(function() {
     console.log("get 1");
     resolve(1);
   }, 1000)
 });
}
a().then(function(value, resolve) {
 console.log("get...");
 setTimeout(function() {
   console.log("get 2");
   resolve(2);
 }, 1000)
}).then(function(value, resolve) {
   console.log(value)
})

控制台得到如下结果:

get... 

get 1

get...

get 2
2

六、async/await

  • then只是将callback拆分了
  • async/await是最直接的同步写法

async/await的用法

  • 使用await,函数必须有async标识
  • await后跟的是一个Promise实例
  • 需要babel-polyfill
import 'babel-polyfill'

function loadImg(src) {
    var promise = new Promise(function(resolve, reject){
        var img = document.createElement('img')
        //throw new Error('自定义错误')
        img.onload = function(){
            resolve(img)
        }
        img.onerror = function(){
            reject('图片加载失败!')
        }
        img.src = src
    })
    return promise
}

var src1 = 'https://img11.360buyimg.com/n1/s450x450_jfs/t3427/221/1828863550/93027/757e93ac/5832861fN2566f854.jpg'

var src2 = 'https://img10.360buyimg.com/mobilecms/s250x250_jfs/t22588/177/2455233485/268618/d7383c4f/5b83afceNdbaff291.jpg'

const load = async function(){
    const result1 = await loadImg(src2)
    console.log(result1)
    const result2 = await loadImg(src1)
    console.log(result2)
}
load()

七、jQuery的Deferred、Promise、async/await总结

  • 无法改变JS异步和单线程的本质
  • 只能从写法上杜绝callback这种形式
  • 它是一种语法糖,但是解耦了代码
  • 很好的体现了:开放封闭原则
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容

  • 异步编程对JavaScript语言太重要。Javascript语言的执行环境是“单线程”的,如果没有异步编程,根本...
    呼呼哥阅读 7,308评论 5 22
  • 弄懂js异步 讲异步之前,我们必须掌握一个基础知识-event-loop。 我们知道JavaScript的一大特点...
    DCbryant阅读 2,710评论 0 5
  • 之前介绍了Node.js的事件机制, 也许读者对此尚会觉得意犹未尽,因为仅仅只是简单的事件机制,并不能道尽Node...
    七寸知架构阅读 1,899评论 1 50
  • 距离你中考还有不到一百天的日子,每天和你视频,看到电话那头的你总是一副睡不醒的迷糊样子,就莫名的心疼。我...
    寒酥98阅读 350评论 0 0
  • 经过了高中三年的拼搏,翻过高考这座大山,我们终于走向各自的大学校门,曾经是那么憧憬着大学的生活,觉得那是有趣而...
    随燕之迹阅读 448评论 0 1