同步与异步
1.同步
一次只能执行一个任务,函数调用后需等到函数执行结束,返回执行的结果,才能进行下一个任务。如果这个任务执行的时间较长,就会导致「线程阻塞」。
/* 例2.1 */
var x = true;
while(x);
console.log("don't carry out"); //不会执行
2.js的单线程与异步
我们知道,js的执行环境是「单线程」
说到js的单线程(single threaded)和异步(asynchronous),很多同学不禁会想,这不是自相矛盾么?其实,单线程和异步确实不能同时成为一个语言的特性。js选择了成为单线程的语言,所以它本身不可能是异步的,但js的宿主环境(比如浏览器,Node)是多线程的,宿主环境通过某种方式(事件驱动,下文会讲)使得js具备了异步的属性。
3.浏览器为耗时的任务开辟另外的线程
js是单线程语言,浏览器只分配给js一个主线程,用来执行任务(函数),但一次只能执行一个任务,这些任务形成一个任务队列排队等候执行,但前端的某些任务是非常耗时的,比如http网络请求,定时器和事件监听,如果让他们和别的任务一样,都老老实实的排队等待执行的话,执行效率会非常的低,甚至导致页面的假死。所以,浏览器为这些耗时任务开辟了另外的线程,主要包括http请求线程,浏览器定时触发器,浏览器事件触发线程,这些任务是异步的。
4.任务队列
刚才说到浏览器为网络请求这样的异步任务单独开了一个线程,那么问题来了,这些异步任务完成后,主线程怎么知道呢?答案就是回调函数,整个程序是事件驱动的,每个事件都会绑定相应的回调函数,举个栗子,有段代码设置了一个定时器
setTimeout(function(){
console.log(time is out);
},50);
执行这段代码的时候,浏览器异步执行计时操作,当50ms到了后,会触发定时事件,这个时候,就会把回调函数放到任务队列里。整个程序就是通过这样的一个个事件驱动起来的。
所以说,js是一直是单线程的,浏览器才是实现异步的那个。
要实现按顺序打印
function ajax(word) {
setTimeout(() => {
console.log(word)
},1000)
}
ajax('1')
console.log('2')
上面这段代码,打印出2,1,因为setTimeout是异步,js遇到异步会先挂起,先执行同步任务。
1.用回调的方法解决
function ajax1(word,fn) {
setTimeout(() => {
console.log(word)
fn()
},1000)
}
ajax1('1',() => {
ajax1('2',() => {
ajax1('3',() => {
ajax1('4',() => {
})
})
})
})
如果项目复杂,就会有请求1,请求2,这就是传说中的回调地狱,简称callback hell
2.promise解决方案
function delay(word) {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve(word)
},2000)
})
}
delay('孙悟空')
.then((word) => {
console.log(word)
return delay('猪八戒')
})
.then(word => {
console.log(word)
return delay('沙悟净')
})
.then(word => {
console.log(word)
})
3.async+await
function delay(word) {
return new Promise((resolve,reject) => {
setTimeout(() => {
resolve(word)
},2000)
})
}
//async+await一定要一起使用,async定义函数,await在async内部使用,等待异步执行完毕
async function start() {
const word1 = await delay('孙悟空')
console.log(word1)
const word2 = await delay('猪八戒')
console.log(word2)
const word3 = await delay('沙悟净')
console.log(word3)
}
start()
下面用一个小球运动的例子来描述
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
.ball{
width: 40px;height: 40px;border-radius: 20px;
}
.ball1{
background: red;
}
.ball2{
background: yellow;
}
.ball3{
background: #ccc;
}
</style>
</head>
<body>
<div class="ball ball1" style="margin-left:0"></div>
<div class="ball ball2" style="margin-left:0"></div>
<div class="ball ball3" style="margin-left:0"></div>
<script type="text/javascript">
var ball1 = document.querySelector('.ball1');
var ball2 = document.querySelector('.ball2');
var ball3 = document.querySelector('.ball3');
//用回调的方法写动画
function animate(ball,distance,cb){
setTimeout(function(){
var marginLeft = parseInt(ball.style.marginLeft,10)
if(marginLeft === distance){
cb && cb()
}else{
if(marginLeft < distance){
marginLeft++
}else{
marginLeft--
}
ball.style.marginLeft = marginLeft + 'px'
animate(ball,distance,cb)
}
},13)
}
//调用
animate(ball1,100,function(){
animate(ball2,200,function(){
animate(ball3,300,function(){
animate(ball3,150,function(){
animate(ball2,150,function(){
animate(ball1,150,function(){
})
})
})
})
})
})
//----------------------------------------------------
//promise方法写动画
function promiseAnimate(ball,distance){
return new Promise(function(resolve,reject){
function _animate(){
setTimeout(function(){
var marginLeft = parseInt(ball.style.marginLeft,10);
if(marginLeft === distance){
resolve();
}else{
if(marginLeft < distance){
marginLeft++
}else{
marginLeft--
}
ball.style.marginLeft=marginLeft + 'px'
_animate(ball,distance);
}
},13);
}
_animate();
})
}
//调用
promiseAnimate(ball1,100)
.then(function(){
return promiseAnimate(ball2,200);
})
.then(function(){
return promiseAnimate(ball3,300);
})
.then(function(){
return promiseAnimate(ball3,150);
})
.then(function(){
return promiseAnimate(ball2,150);
})
.then(function(){
return promiseAnimate(ball1,150);
})
//async+await方法
async function start() {
await promiseAnimate(ball1,100)
await promiseAnimate(ball2,200)
await promiseAnimate(ball3,150)
await promiseAnimate(ball2,150)
await promiseAnimate(ball1,150)
}
start()
</script>
</body>
</html>
简单的ajax实现方法
//--------------------回调-------------------------------------------------------------------------
var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';
var result;
var XHR = new XMLHttpRequest();
XHR.open('GET', url, true);
XHR.send();
XHR.onreadystatechange = function() {
if (XHR.readyState == 4 && XHR.status == 200) {
result = XHR.response;
console.log(result);
// 伪代码
var url2 = 'http:xxx.yyy.com/zzz?ddd=' + result.someParams;
var XHR2 = new XMLHttpRequest();
XHR2.open('GET', url, true);
XHR2.send();
XHR2.onreadystatechange = function() {
//...
}
}
}
//---------promise----------------------------------------------------------------------------------------
var url = 'https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10';
// Promise封装一个get请求的方法
function getJSON(url) {
return new Promise(function(resolve, reject) {
var XHR = new XMLHttpRequest();
XHR.open('GET', url, true);
XHR.send();
XHR.onreadystatechange = function() {
if (XHR.readyState == 4) {
if (XHR.status == 200) {
try {
var response = JSON.parse(XHR.responseText);
resolve(response);
} catch (e) {
reject(e);
}
} else {
reject(new Error(XHR.statusText));
}
}
}
})
}
getJSON(url).then(resp => console.log(resp));
//-----------------------async------------------------------------------------------------------------
function getUrl(url) {
return new Promise((resolve,reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET',url,true)
xhr.send()
xhr.onreadystatechange = function() {
if(xhr.readyState == 4){
if(xhr.status == 200) {
resolve(JSON.parse(xhr.response))
}else{
reject(new Error(XHR.statusText));
}
}
}
})
}
async function start() {
const word1 = await getUrl('https://hq.tigerbrokers.com/fundamental/finance_calendar/getType/2017-02-26/2017-06-10')
console.log(word1)
}
start()
网友评论