JS

JS深入浅出之异步

Posted by weite122 on 2018-02-16

同步与异步

  • 同步:等待结果

  • 异步:不等待结果

  • 注意,异步常常伴随回调一起出现,但是异步不是回调,回调也不一定是异步。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

// 同步的 sleep
function sleep(seconds){
var start = new Date()
while(new Date() - start < seconds * 1000){

}
return
}
console.log(1)
sleep(3)
console.log('wake up')
console.log(2)
同步的sleep

Fk3uIFNUM-XyzClsAyB5MzmZzQki.jpg

1
2
3
4
5
6
7
//异步的 sleep
function sleep(seconds, fn){
setTimeout(fn, seconds * 1000)
}
console.log(1)
sleep(3, ()=> console.log('wake up'))
console.log(2)

FuYkM9AJekunsdsrsmTumIUfWPg0.jpg

  • 可以看出,用了异步之后,JS 的空闲时间,多了许多。

  • 但是注意,在 JS 空闲的这段时间,实际上是浏览器中的计时器在工作(很有可能是每过一段时间检查是否时间到了,具体要看 Chrome 代码)

前端经常遇到的异步

1
2
3
4
5
6
7
document.getElementsByTagNames('img')[0].width // 宽度为 0
console.log('done')
document.getElementsByTagNames('img')[0].onload = function(){
console.log(this.width) // 宽度不为 0
console.log('real done')
}
console.log('done')

面试题中的异步

1
2
3
4
5
6
7

let liList = document.querySelectorAll('li')
for(var i=0; i<liList.length; i++){
liList[i].onclick = function(){
console.log(i)
}
}

AJAX 中的异步

1
2
3
4
5
6
7
8
9
10
11
12
let request = $.ajax({
url: '.',
async: false
})
console.log(request.responseText)
$.ajax({
url: '/',
async: true,
success: function(responseText){
console.log(responseText)
}
})

异步的形式

  • 一般有两种方式拿到异步结果

    • 傻逼方法:轮询
    • 正规方法:回调

回调的形式

  1. Node.js 的 error-first 形式
1
2
3
4
5
6
7
fs.readFile('./1.txt', (error, content)=>{
if(error){
// 失败
}else{
// 成功
}
})
  1. jQuery 的 success / error 形式
1
2
3
4
5
6

$.ajax({
url:'/xxx',
success:()=>{},
error: ()=>{}
})
  1. jQuery 的 done / fail / always 形式
1
2
3
4
5
6
7
 $.ajax({
url:'/xxx',
}).done( ()=>{} ).fail( ()=>{} ).always( ()=> {})
Prosmise 的 then 形式
$.ajax({
url:'/xxx',
}).then( ()=>{}, ()=>{} ).then( ()=>{})

如何处理异常?

  1. 如何使用多个 success 函数?
    • 当函数第一次获取到结果时,不管是success1处理还是error1处理,只要没有报错,都会给success2函数处理,如果报错了就会给error2处理,如果success2和error2处理成功,没有报错,下一次会交给success3处理,反之,交给error处理
      深度截图_选择区域_20180304144719.png
  • code:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <!DOCTYPE html>
    <html>

    <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.17.1/axios.min.js"></script>
    <meta charset="utf-8">
    <title>JS Bin</title>
    </head>

    <body>
    <script>
    axios({
    url: '',
    async: true,
    }).then((x)=>{
    console.log('成功')
    alert(xxxxx)
    }, (y)=>{
    console.log('失败')
    }).then((x)=>{
    console.log('成功2')
    },(y) => {
    console.log('失败2')
    console.log(y)
    })
    </script>
    </body>

    </html>
  1. 在有多个成功回调的情况下,如何处理异常?
  • 如果出现异常就会返回给下一个函数的error,可以使用catch或者直接返回一个兜底函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.17.1/axios.min.js"></script>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<script>
axios({
url: '',
async: true,
}).then((x)=>{
console.log('成功')
return Promise.reject('reject1111') //1.直接返回reject函数处理他
}, (y)=>{
console.log('失败')
}).then((x)=>{
console.log('成功2')
}).catch((x)=>{ //2. 用catch函数,catch等价于没有成功函数的then,可以替换为then(undefined, (x)=>{...})
console.log('catch')
console.log(x)
})
</script>
</body>
</html>

自己返回 Promise

1
2
3
4
5
6
7
8
9
10
function ajax(){
return new Promise((resolve, reject)=>{
做事
如果成功就调用 resolve
如果失败就调用 reject
})
}

var promise = ajax()
promise.then(successFn, errorFn)

async / await

1
2
3
4
5
6
7
function buyFruit(){
return new Promise((resolve, reject)=>{
做事
如果成功就调用 resolve
如果失败就调用 reject
})
}
1
2
3
4
5
6
7
8
var promise = await ajax()
async functon fn(){
var result = await buyFruit()
return result
}

var r = await fn()
console.log(r)

实现Promise的简化版

1
2
3
4
5
6
7
8
9
10
11
12
13
function Promise(???){
???
return ???
}

var promise = new Promise(function(x,y){
setTimeout(()=>{
x(101)
}, 3000)
})
promise.then((z)=>{
console.log(z) // 101
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function Promise(fn){
var status = 'pending'
function successNotify(){
status = 'resolved'
toDoThen.apply(undefined, arguments)
}
function failNotify(){
status = 'rejected'
toDoThen.apply(undefined, arguments)
}
function toDoThen(){
setTimeout(()=>{ // 保证回调是异步执行的
if(status === 'resolved'){
for(let i =0; i< successArray.length;i ++) {
successArray[i].apply(undefined, arguments)
}
}else if(status === 'rejected'){
for(let i =0; i< failArray.length;i ++) {
failArray[i].apply(undefined, arguments)
}
}
})

}
var successArray = []
var failArray = []
fn.call(undefined, successNotify, failNotify)
return {
then: function(successFn, failFn){
successArray.push(successFn)
failArray.push(failFn)
return undefined // 简化
}
}
}