JS的异步方法

2020-09-12  本文已影响0人  壹豪

本文是我写的一个目录,具体内容会跳转我的其他文章,阅读性可能较差,抱歉

众所周知,JS是单线程运行的,同一时刻只能执行一个任务,如果碰到一个执行时间很长的任务,那么后面所有的任务都需要排队,这样效率过于低下,因此诞生了异步,虽然还是只能执行一个任务,但是可以把耗时长优先级低的任务往后排。
我们按出现的时间顺序来介绍不同的异步方法,便于记忆。

1.回调函数(ES5)

直接上代码:

function foo1(callback){
    setTimeout(function(){
        callback();
    }, 1000)
}
function foo2(){
    let i = 10000;
    while(i){
        console.log(i--);
    }
}

foo1(foo2);
console.log('urgent task')

函数 foo2() 需要很久执行完毕,但是在后面有一个紧急任务console.log('urgent task')需要赶紧执行,那么就使用 foo1() 设置一个定时器 setTimeout() 来回调 foo2() 。原理是利用JS的 event loop 运行机制,利用定时器 setTimeout() 把 foo2() 插入宏任务队列,等下次执行宏任务时才运行。

优点:简单,易部署
缺点:不易于维护和阅读,高度耦合

2.事件监听

这种方式下,异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

下面是两个函数 f1 和 f2,编程的意图是 f2 必须等到 f1 执行完成,才能执行。首先,为 f1 绑定一个事件(这里采用的 jQuery 的写法)

f1.on('done', f2);

上面这行代码的意思是,当 f1 发生 done 事件,就执行 f2。然后,对f1进行改写:

function f1() {
  setTimeout(function () {
    // ...
    f1.trigger('done');
  }, 1000);

上面代码中,f1.trigger(‘done’)表示,执行完成后,立即触发 done 事件,从而开始执行f2。

这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以"去耦合",有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。阅读代码的时候,很难看出主流程。

3.发布订阅

我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做"发布/订阅模式"(publish-subscribe pattern),又称"观察者模式"(observer pattern)。

首先,f2 向信号中心 jQuery 订阅 done 信号。

jQuery.subscribe('done', f2);

然后,f1进行如下改写:

function f1() {
  setTimeout(function () {
    // ...
    jQuery.publish('done');
  }, 1000);
}

上面代码中,jQuery.publish(‘done’)的意思是,f1执行完成后,向信号中心 jQuery 发布 done 信号,从而引发f2的执行。
f2 完成执行后,可以取消订阅(unsubscribe)

jQuery.unsubscribe('done', f2);

这种方法的性质与“事件监听”类似,但是明显优于后者。因为可以通过查看“消息中心”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。

4.Promise

见我的文章Promise总结

5.Generator / yield

Generator 是ES6里新出的一个语法
在 function 后加上一个 * 即代表是一个 Generator 构造器,然后在 Generator 构造器中使用 yield 语法配合使用。每次使用 Generator 构造器的 next() 方法就会执行 Generator 的代码,然后每次遇到 yield x 就返回一个对象 {value: x, done: true/false},然后“暂停”。返回的 value 就是 yield 后面表达式的返回值,done 表示这个 Generator 是否已经执行结束了。如果 done 为 true,则 value 就是 return 的返回值。

调用 Generator 对象有两个方法:

  1. 不断地调用 Generator 对象的 next() 方法
function* fib(max) {
    var
        t,
        a = 0,
        b = 1,
        n = 0;
    while (n < max) {
        yield a;
        [a, b] = [b, a + b];
        n ++;
    }
    return;
}

var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: false}
f.next(); // {value: undefined, done: true}
  1. 直接用 for ... of 循环迭代 Generator 对象
'use strict'
//斐波拉契数列
function* fib(max) {
    var
        t,
        a = 0,
        b = 1,
        n = 0;
    while (n < max) {
        yield a;
        [a, b] = [b, a + b];
        n ++;
    }
    return;
}

for (var x of fib(10)) {
    console.log(x); // 依次输出0, 1, 1, 2, 3, ...
}
6.async / await

见我的文章超详细的 async / await

[1].JS 异步编程六种方案
[2].generator - 廖雪峰的官方网站

上一篇 下一篇

猜你喜欢

热点阅读