每周获取最新的 Wordpress 资源

Javascript 异步编程简介

Web 开发中,异步编程是一个绕不开的话题,应用非常广泛,这篇文章介绍异步编程常见的方式和方法。

同步和异步编程的区别

我们都知道 Javascript 语言的执行环境是 “单线程”(single thread)。导致了 Javascript 应用程序在执行多任务时,遇到耗时任务,会延迟整个程序执行,导致浏览器假死。

为了解决这个问题,Javascript 语言将任务的执行模式分成两种:同步(Synchronous)和异步(Asynchronous)。

同步:后一个任务等待前一个任务结束,然后再执行,程序的执行顺序与任务的排列顺序是一致的、同步的

异步:提交后继续执行后面的代码,同时在后台继续监听,服务器响应后有程序作相应处理,异步的操作好处是不必等待服务器而可以继续在客户端做其他事情

Javascript 异步加载又叫非阻塞,浏览器在下载执行 JS 同时,还会继续进行后续页面的处理。这种方法是在页面中 <script> 标签内,用 JS 创建一个 script 元素并插入到 document 中。这样就做到了非阻塞的下载 JS 代码。

<script> 标签有两个异步加载相关的属性,即 deferasync 属性。

defer 属性,声明这个脚本中将不会有 document.writeDOM 操作

<script src="..." defer></script>

async 属性,HTML5 中新增的异步支持

<script src="..." async></script>

或者

<script>
    (function() {
        var s = document.createElement('srcipt');
        s.type = 'text/javascript';
        s.async = true;
        s.src = 'http://wordpresshi.com/a.js';
        var x = document.getElementsByTagName('script')[0];
        x.parentNode.insertBefore(s,x);
    })();
</script>

异步编程方法

  • 回调函数
  • 事件监听
  • 发布订阅模式/自定义事件
  • Promise
  • Generator
  • Async/Await(基于 Promise)

回调函数

回调函数是异步编程最基本的方法,但是容易出现回调地域的问题,维护起来非常不方便。这就出现了后来的 Promise,Generator 和 Async/Await 解决方案。

事件监听

任务的执行不取决于代码的顺序,而取决于某个事件是否发生。在 Javascript 原生 DOM 中,这样设置监听器:

target.addEventListener(type, listener, options);
target.addEventListener(type, listener, useCapture);
  • type 表示监听事件类型的字符串
  • listener 当所监听的事件类型出发时,会接受到一个事件通知对象
  • options 一个指定有关 listener 属性的可选参数对象
  • useCapture 布尔值,true 代表使用捕获,false(默认值) 代表使用冒泡

具体查看 EventTarget.addEventListener()

发布订阅(观察者模式)

存在一个“信号中心”,某个任务执行完成,就向信号中心“发布”(publish)一个信号,其他任务可以向信号中心“订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。

观察者模式或者说发布者-订阅模式,包含

  • 观察者(订阅者)
  • 发布者(观察的目标)

其中发布者会保存一份观察者(订阅者)的名单,发生改变的时候,会主动向订阅者发出通知

以我们经常使用的事件为例子

document.body.addEventListener('click', function() {
    alert('我是一个观察者,你一点击,我就知道了')
})

这其中 body 就是发布者,被观察的目标,addEventListener 是发布者的一个方法,用来监听改变然后发布通知的,addEventListenrt 的回调函数就是订阅者,接收到更新后就做出相应的动作,这个 JavaScript 原生 DOM API 实现的,可以很灵活地观察我们指定 DOM 状态的变化,但是如果我们要观察非 DOM 元素该怎么办呢?

// 观察者
function Observer() {
    // 更新操作
    this.update = function() {
        // ...
    }
}

var mo = new Observer('墨');
var lang = new Observer('朗');


// Subject 观察的目标
function Subject() {
    this.observers = [];
}

// 增加观察者
Subject.prototype.addObserver = function(observer) {
    this.observers.push(observer);
}

// 去除观察者
Subject.prototype.removeObserver = function(observer) {
    var index = this.observers.indexOf(observer);
    this.observers.splice(index, 1);
}

// 通知观察者
Subject.prototype.notify = function(context) {
    var observerLength = this.obervers.length;
    // 循环调用所有观察者 update
    for (var i = 0; i < observerLength; i++) {
        this.observers[i].update(context);
    }
}


var sub = new Subject('主播 ID');

// 增加观察者
sub.addObserver(mo);
sub.addObserver(lang);

sub.notify('有更新');

优点:

  • 更加解耦
  • 支持广播通信

缺点:

  • 大量观察者,广播有性能问题

Promise

Promise 是 CommonJS 规范,为异步编程提供统一接口。

它的思想是,每一个异步任务返回一个 Promise 对象,该对象有一个 then 方法,允许指定回调函数。比如,f1 的回调函数 f2,可以写成:f1.then(f2);

new Promise(function(resolve, reject) {
    resolve('成功');
    reject('失败');
}).then(info => {
    console.log(info);
}).catch(err => {
    console.log(err);
)

Generator

function 关键字后面加个星号 *,星号表示该函数就是 Generator!

function* getEmployee() {
    console.log('the function has started');

    const names = ['Amanda', 'Diego', 'Farrin', 'James', 'Kagure', 'Kavita', 'Orit', 'Richard'];

    for (const name of names) {
        console.log( name );
    }

    console.log('the function has ended');
}

该函数会返回一个对象供下一步操作。

我们可以使用关键字 yield 让 Generator 暂停,同时可以从 Generator 中获取数据。我们还可以将数据发送回生成器中。方式是使用 .next() 方法

function* displayResponse() {
    const response = yield;
    console.log(`Your response is "${response}"!`);
}

const iterator = displayResponse();

iterator.next(); // 开始运行生成器函数
iterator.next('Hello Udacity Student'); // 将数据发送到生成器中
// 上面的一行打印到控制台:你的响应是 "Hello Udacity Student"!

Generator 可以说是一种过渡阶段的产物,现在更多的会使用接下来要讲的 Async/Await

Async/Await

当调用一个 async 函数时,会返回一个 Promise 对象。当这个 async 函数返回一个值时,Promise 的 resolve 方法会负责传递这个值;当 async 函数抛出异常时,Promise 的 reject 方法也会传递这个异常值。

async 函数中可能会有 await 表达式,这会使 async 函数暂停执行,等待表达式中的 Promise 解析完成后继续执行 async 函数并返回解决结果。

更多参考:async function

总结

这里只是简单介绍了前端中异步编程常见的方式和方法,每一个展开的话,有不少东西值得探究。

You May Also Like

About the Author: ted

发表评论

电子邮件地址不会被公开。 必填项已用*标注