JavaScript中异步编程

一 关于事件的异步

事件是JavaScript中最重要的一个特征,nodejs就是利用js这一异步而设计出来的。所以这里讲一下事件机制。

在一个js文件中,如果要运行某一个函数,有2中手段,一个就是直接调用,比如foo(),第二就是利用事件来触发,这中函数也叫回调函数,比如传递给setTimeout函数和onready属性。

1.setTimeout函数中的事件异步

setTimeout本质上也是一种异步事件,当延迟时间到的时候触发该事件,但是有的有的时候(其实也是大部分时候)都不会按照给定的延迟时间执行,先看下面的代码

var start = new Date();
        setTimeout(function() {
            console.log('settimeout1:',new Date()-start);
        }, 500);
        while (new Date() - start < 1000) {
            console.log('in while');
        }
        document.getElementById('test').addEventListener('click', function(){
            console.log('test:',new Date()-start);
        }, false)
        for(var i=0;i<10000;i++){
            console.log('in for');
        }
        setTimeout(function(){
            console.log('settimeout2: ',new Date()-start);
        },1000);
        /* 10214
        in while
        index.jsp (第 19 行)
        10000
        in for
        index.jsp (第 25 行)
        settimeout1: 2263
        index.jsp (第 16 行)
        settimeout2: 3239
        index.jsp (第 28 行)
        test: 10006
        index.jsp (第 22 行)
        test: 28175
        index.jsp (第 22 行)
        test: 28791
        index.jsp (第 22 行)
        test: 28966
        index.jsp (第 22 行) */

如果按照正常的理解,延迟函数应该在500毫秒之后打断while循环,而事实上并没有,并且,我在while循环和for循环期间点击div时候并没有立即输出test,给出的解释就是:

a)事件队列。调用setTimeout函数的时候,会把传入它的回调函数加入到事件队列中去(事件已经初始化并且在内存了),然后继续执行后面的代码,直到再也没有代码可以运行(没有正常的运行流了,不包括事件函数等异步的内容),就会从事件队列里面pop出一个合适的事件来运行。

b)js是单线程的,事件处理器在线程空闲之前是不会运行的。

2 普通事件的异步和setTimeout类似

二 promise对象和deferred对象

1. promise

promise是一种解决ajax等异步编程回调函数嵌套太多导致代码晦涩难懂的解决方案,特别是在nodejs中,异步无处不在。不同的框架对promise的实现,一下是jquery中的promise的API。

这里不讲promise的实现原理,关于原理在另外的篇幅中介绍。

传统的ajax异步编程是这么写的(jquery1.5之前):

$.get('url', function(){
    $.get('url1', function(){
        $.get('url2', function(){

}, 'json');
    }, 'json');
}, 'json');

这么写代码给开发和维护带来了极大的困难,好在jquery1.5以后引入了promise,就可以这么写了:

$.ajax( "example.php" )
.done(function() { alert("success"); })
.fail(function() { alert("error"); })
.always(function() { alert("complete"); });

现在看上去就明显简单多了。

2.deferred对象

var nanowrimoing = $.Deferred();
var wordGoal = 5000;
nanowrimoing.progress(function(wordCount) {
var percentComplete = Math.floor(wordCount / wordGoal * 100);
$('#indicator').text(percentComplete + '% complete');
});
nanowrimoing.done(function(){
$('#indicator').text('Good job!');
});

三.worker对象和多线程

四.异步脚本加载

1.传统脚本在页面中的位置

脚本分为两大类:阻塞式和非阻塞式。这里的阻塞是指加载阻塞而不是运行阻塞。

<!DOCTYPE html>
<html>
<head>
<script src="https://www.linuxidc.com/headScript"></script>
<script defer src="https://www.linuxidc.com/deferredScript"></script>
</head>
<body>
    <script async defer src="https://www.linuxidc.com/chatWidget"></script>
    <script async defer src="https://www.linuxidc.com/asyncScript"></script>
</body>
</html>

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/51c88ca7dd8050078da0388b9ca4c6f1.html