乐趣区

JavaScript-异步编程035

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

浏览器渲染进程图:

异步机制

 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…

退出移动版