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…