欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

JavaScript异步编程

程序员文章站 2022-03-09 08:33:24
...

JavaScript 异步编程

一道面试题

for(var i = 0; i < 3; i++) {
   setTimeout(function() {
       console.log('timeout' + i);
   })
}

new Promise(function(resolve) {
    console.log('promise1');
    for(var i = 0; i < 1000; i++) {
        i == 99 && resolve();
    }
    console.log('promise2');
}).then(function() {
    console.log('then1');
})

console.log('global1');


promise1
promise2
global1
then1
3 timeout3

JavaScript单线程

在浏览器的一个页面中,该页面的JS程序只有一个线程,故曰单线程。因为是单线程,所以程序的执行顺序就是从上到下依次执行,同一时间内只能有一段代码被执行。

浏览器多进程

  • 浏览器是多进程的
  • 浏览器之所以能够运行,是因为系统给它的进程分配了资源(cpu、内存)
  • 简单点理解,每打开一个Tab页,就相当于创建了一个独立的浏览器进程。
  • 浏览器的渲染进程是多线程的
    • GUI渲染线程
    • JS引擎线程
    • 事件触发线程
    • 定时触发器线程
    • 异步http请求线程

浏览器渲染进程图:
JavaScript异步编程

异步机制

 for (var i = 0; i < 5; i ++) {
        setTimeout(function(){
            console.log(i);
        }, 0);
    }
    console.log(i);
    //5 ; 5 ; 5 ; 5; 5

回调函数Callback

这是异步编程最基本的方法。

假定有两个函数f1和f2,后者等待前者的执行结果。

  f1();

  f2();

如果f1是一个很耗时的任务,可以考虑改写f1,把f2写成f1的回调函数。\

  function f1(callback){
    setTimeout(function () {
      // f1的任务代码
      callback();
    }, 1000);
  }

执行代码就变成下面这样:

 f1(f2);

采用这种方式,我们把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。

回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合Coupling,流程会很混乱,而且每个任务只能指定一个回调函数。

发布订阅

发布-订阅模式又叫做观察者模式,他定义了一种一对多的依赖关系,即当一个对象的状态发生改变的时候,所有依赖他的对象都会得到通知。

let yourMsg = {};
yourMsg.peopleList = [];
yourMsg.listen = function (fn) {
    this.peopleList.push(fn);
}
yourMsg.triger = function () {
    for(var i = 0,fn;fn=this.peopleList[i++];){
        fn.apply(this,arguments);
    }
}

yourMsg.listen(function (name) {
    console.log(`${name}收到了你的消息`);
})
yourMsg.listen(function (name) {
    console.log('哈哈');
})

yourMsg.triger('张三');
yourMsg.triger('李四');

Promise

Promise 是一个对象,它代表了一个异步操作的最终完成或者失败。

Promise 最直接的好处就是链式调用(chaining

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

生成器Generators/ yield

Generator

function* Hello() {
    yield 100
    yield (function () {return 200})()
    return 300
}

var h = Hello()
console.log(typeof h)  // object

console.log(h.next())  // { value: 100, done: false }
console.log(h.next())  // { value: 200, done: false }
console.log(h.next())  // { value: 300, done: true }
console.log(h.next())  // { value: undefined, done: true }

yield

yield关键字使生成器函数执行暂停,yield关键字后面的表达式的值返回给生成器的调用者。它可以被认为是一个基于生成器的版本的return关键字。

function* countAppleSales () {
  var saleList = [3, 7, 5];
  for (var i = 0; i < saleList.length; i++) {
    yield saleList[i];
  }
}
var appleStore = countAppleSales(); // Generator { }
console.log(appleStore.next()); // { value: 3, done: false }
console.log(appleStore.next()); // { value: 7, done: false }
console.log(appleStore.next()); // { value: 5, done: false }
console.log(appleStore.next()); // { value: undefined, done: true }

async/await

async function func() {
    try {
        let res = await asyncFunc()
    } catch (e) {
      //......
    }
}

async 函数返回的 Promise 对象,必须等到内部所有的 await 命令的 Promise 对象执行完,才会发生状态改变

async 函数的语法不难,难在错误处理上。

https://juejin.im/post/5a6547d0f265da3e283a1df7

http://www.ruanyifeng.com/blog/2012/12/asynchronous_javascript.html

https://juejin.im/post/5a16810d6fb9a0450e75c958

https://juejin.im/post/5ce75fe16fb9a07eb67d6999

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

https://juejin.im/post/596e142d5188254b532ce2da