zoukankan      html  css  js  c++  java
  • 异步与回调

    异步和回调

    这两个最好还是分开来说

    回调

    var b = function (){
        //执行相关的代码
    }
    var a = function (b){
        //执行相关的代码
        b();
    }
    
    a(b);
    

    这就是回调,不过一般的套路都是

    var a = function (callback){
        if(callback&&typeof callback === 'function'){
            callback(something)
        }
    }
    

    这是很多回调才用写法。

    异步

    异步就是因为js的单线程,如果总是才用同步的话,那么js就会出现因为某一步特别耗时,所以导致后面的与之无关程序都被阻塞。

    至于实现的原理,与settimeout是类似的,在浏览器运行到异步的地方的时候会做一个标记,在settimeout中是dalaytime,在ajax中是status,那么在标记约定的时候的就会处理键入的函数。

    异步与回调其实没有什么直接关系,但是如果你只是单纯读文件,或者获取一个数据,而不对数据做任何处理的话,是不需要回调的,但是,如果需要对返回的事情做处理,那么回调就是必须的了。

    而在回调中所举的例子其实是一种同步的回调,因为它没有采用异步的方法,用settimeout可以完成改写。

    function a(m){
        setTimeout(function(){
            b(m);
        },0);
        m = m + 1;
        console.log(m)
    }
    function b(n){
        console.log(n+1)
    }
    console.log(a(1))
    

    结果是

    2

    3

    这就体现了异步的概念,程序运行到setTimeout之后,创建了一个函数,延迟执行b,而且继续执行下面,等到当前函数执行完成,然后输出m为2,运行结束后,执行之前标记的函数,输出3。

    这里必须说一下事件循环,才好理解js的机制。

    事件循环 Event Loop

    js里有两个结构:

    • 消息队列(message queue,or task queue):储待处理消息及对应的回调函数或事件处理程序;
    • 执行栈(execution context stack),也就是用来存储执行上下文,当函数调用时,创建并插入一个执行上下文,通常称为执行栈帧(frame),存储着函数参数和局部变量,当该函数执行结束时,弹出该执行栈帧;

    关于全局代码,由于所有的代码都是在全局上下文执行,所以执行栈顶总是全局上下文就很容易理解,直到所有代码执行完毕,全局上下文退出执行栈,栈清空了;也即是全局上下文是第一个入栈,最后一个出栈。

    1. 宿主环境为JavaScript创建线程时,会创建堆(heap)和栈(stack),堆内存储JavaScript对象,栈内存储执行上下文;
    2. 栈内执行上下文的同步任务按序执行,执行完即退栈,而当异步任务执行时,该异步任务进入等待状态(不入栈),同时通知线程:当触发该事件时(或该异步操作响应返回时),需向消息队列插入一个事件消息;
    3. 当事件触发或响应返回时,线程向消息队列插入该事件消息(包含事件及回调);
    4. 当栈内同步任务执行完毕后,线程从消息队列取出一个事件消息,其对应异步任务(函数)入栈,执行回调函数,如果未绑定回调,这个消息会被丢弃,执行完任务后退栈;
    5. 当线程空闲(即执行栈清空)时继续拉取消息队列下一轮消息(next tick,事件循环流转一次称为一次tick)。

    这就是事件循环,js的函数就是如此执行的。

    事件监听

    也就是事件驱动模式,任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

    f1.on('done', f2);
    function f1(){
    

        setTimeout(function () {
          // f1的任务代码
          f1.trigger('done');
        }, 1000);
      }

    一下是事件监听的实现方法

    // 事件对象
    var Event = function(obj) {
        this.obj = obj;
        this.getSource = function() {
            return this.obj;
        }
    }
    // 监听对象
    var F2 = function() {
        this.hander = function(event) {
            var f2 = event.getSource();
            console.log("f2 do something!");
            f2.callback();
        }
    }
    // 被监听对象
    var F1 = function() {
        this.abc = function() {
            console.log("f1 do something one!");
            // 创建事件对象
            var e = new Event(this);
            // 发布事件
            this.f2.hander(e);
            console.log("f1 do something two!");
        }
    
        this.on = function(f2) {
            this.f2 = f2;
        }
    
        this.callback = function() {
            console.log("f1 callback invoke!");
        }
    }
    // 主函数
    function main() {
        var f1 = new F1();
        var f2 = new F2();
        // 加入监听
        f1.on(f2);
        f1.abc();
    }
    // 运行主函数
    main();
    

    f1 do something one!

    f2 do something!

    f1 callback invoke!

    f1 do something two!

    发布/订阅

    与事件监听类似,多了个消息中心,可以查看每个对象绑定了多少事件

    promise

    即es6 的 promise

    浏览器多线程和并发

    浏览器不是单线程的 上面说了这么多关于JavaScript是单线程的,下面说说其宿主环境——浏览器。 浏览器的内核是多线程的,它们在内核制控下相互配合以保持同步,一个浏览器至少实现三个常驻线程:

    1. JavaScript引擎线程 JavaScript引擎是基于事件驱动单线程执行的,JavaScript 引擎一直等待着任务队列中任务的到来,然后加以处理。
    2. GUI渲染线程 GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。
    3. 浏览器事件触发线程 事件触发线程,当一个事件被触发时该线程会把事件添加到“任务队列”的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JavaScript是单线程执行的,所有这些事件都得排队等待JavaScript引擎处理。

    在Chrome浏览器中,为了防止因一个标签页奔溃而影响整个浏览器,其每个标签页都是一个进程(Renderer Process)。当然,对于同一域名下的标签页是能够相互通讯的,具体可看 浏览器跨标签通讯。在Chrome设计中存在很多的进程,并利用进程间通讯来完成它们之间的同步,因此这也是Chrome快速的法宝之一。对于Ajax的请求也需要特殊线程来执行,当需要发送一个Ajax请求时,浏览器会开辟一个新的线程来执行HTTP的请求,它并不会阻塞JavaScript线程的执行,当HTTP请求状态变更时,相应事件会被作为回调放入到“任务队列”中等待被执行。

  • 相关阅读:
    FarPoint FpSpread控件的使用收藏
    在Oracle中使用Guid
    oracle 语句的妙用例子
    让服务器iis支持.apk文件下载的设置方法
    oracle 自动生存清库脚本
    winform 消息通讯组件实习
    在css中使用边框做三角形
    JavaScript闭包和ajax
    JavaScript面向对象
    正则表达式
  • 原文地址:https://www.cnblogs.com/mydia/p/6905699.html
Copyright © 2011-2022 走看看