1

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请求线程

浏览器渲染进程图:
img

异步机制

 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/5a6547...
http://www.ruanyifeng.com/blo...
https://juejin.im/post/5a1681...
https://juejin.im/post/5ce75f...
https://developer.mozilla.org...
https://juejin.im/post/596e14...

xiaoping
337 声望12 粉丝

保持学习,记一下自己的学习经历