閱讀時間:
12 minutes
文章型別:理論知識 & 案例演示 案例需求:用JavaScript實現,3個小球先後運動,完成接力賽跑 案例原始碼:見文章最後
引言:
前端開發中,非同步處理必不可少;
過去,我們經常用回撥函式來完成非同步處理,因此也經常產生回撥地獄(callback hell
);
今天,我們用例項來對比非同步處理的方法;
是時候用async
來處理我們的非同步流程了。
Step 1: 回撥函式 Callback
回撥函式: 是將一個函式作為引數,傳遞給另一個函式,然後在外部函式中呼叫該函式來完成某種例程或動作。
用法:在函式內部呼叫函式
2. 用callback
實現小球移動的方法;
function move(ele, target, callback) {
let left = parseInt(getComputedStyle(ele)["left"]);
let timer = setInterval(function () {
if (left >= target) {
clearInterval(timer);
callback();
} else {
left += 2;
ele.style.left = left + "px";
}
}, 4)
}
複製程式碼
2. 執行運動;
move(ball1, 200, function () {
move(ball2, 400, function () {
move(ball3, 600, function () {
alert("callback");
});
})
})
複製程式碼
Step 2: Promise物件Promise
Promise
: 是一個返回值的代理,它允許您將處理程式與非同步操作的最終成功值或失敗原因相關聯。 這使非同步方法可以像同步方法那樣返回值:不是立即返回最終值,而是非同步方法返回一個Promise
,以便在未來的某個時間點提供該值。用法:
Promise
物件是由關鍵字new
及其建構函式來建立的。該函式接收一個函式(executor function
)作為它的引數。這個函式接受兩個函式——resolve
和reject
——作為其引數。當非同步任務順利完成且返回結果值時,會呼叫resolve
函式;而當非同步任務失敗且返回失敗原因(通常是一個錯誤物件)時,會呼叫reject
函式。
1. 用Promise
實現小球移動的方法;
// 讓move方法擁有Promise功能
function move(ele, target) {
return new Promise(function (resolve, reject) {
let left = parseInt(getComputedStyle(ele)["left"]);
let timer = setInterval(function () {
if (left >= target) {
clearInterval(timer);
resolve();
} else {
left += 2;
ele.style.left = left + "px";
}
}, 4)
})
}
複製程式碼
2. 執行運動,呼叫Promise.then()
方法;
move(ball1, 200).then(function () {
return move(ball2, 400);
}).then(function () {
return move(ball3, 600);
}).then(function () {
alert("promise");
})
複製程式碼
Step 3: Generator物件 Generator
Generator
:生成器函式在函式執行時能暫停,還能從暫停處繼續執行,相當於將函式分段執行。
用法:必須用
.next()
配合yield
關鍵字使用;例如: function *gen(){ yield 10; y=yield 'foo'; yield y; } var gen_obj=gen(); console.log(gen_obj.next()); // 執行 yield 10,返回 10 console.log(gen_obj.next()); // 執行 yield 'foo',返回 'foo' console.log(gen_obj.next(10)); // 將 10 賦給上一條 yield 'foo' 的左值,即執行 y=10,返回 10 console.log(gen_obj.next()); // 執行完畢,value 為 undefined,done 為 true
1. 用Genertor
實現小球移動的方法;
// 函式move方法呼叫上面Promise中的move方法;
function move(ele, target) {
return new Promise(function (resolve, reject) {
let left = parseInt(getComputedStyle(ele)["left"]);
let timer = setInterval(function () {
if (left >= target) {
clearInterval(timer);
resolve();
} else {
left += 2;
ele.style.left = left + "px";
}
}, 4)
})
}
複製程式碼
2. 執行運動,需要分佈執行,但此方法需要手動分行執行;
let g = m();
g.next(); //讓第一個小球運動;
g.next(); //讓第二個小球運動;
g.next(); //讓第三個小球運動;
複製程式碼
3. 使用co
庫迭代generator
執行器;
function co(it) {
return new Promise(function (resolve, reject) {
function next(d) {
let { value, done } = it.next(d);
if (!done) {
value.then(function (data) {
next(data)
}, reject)
} else {
resolve(value);
}
};
next();
});
}
// 一行程式碼實現函式執行,但是需要引入co庫;
co(m()).then(function () {
alert("generator");
})
複製程式碼
Step 4: async/await函式 async/await
async
: 非同步函式宣告定義了一個非同步函式,它返回一個AsyncFunction
物件。當async
函式執行,返回一個Promise
物件;用法:用
async
宣告函式,函式內配合await
使用。
1. 用 async/await
實現小球移動的方法;
// 呼叫上面的move()方法;
function move(ele, target) {
return new Promise(function (resolve, reject) {
let left = parseInt(getComputedStyle(ele)["left"]);
let timer = setInterval(function () {
if (left >= target) {
clearInterval(timer);
resolve();
} else {
left += 2;
ele.style.left = left + "px";
}
}, 4)
})
}
複製程式碼
2. 執行運動,函式內await
方法;
async function a() {
await move(ball1, 200);
await move(ball2, 400);
await move(ball3, 600);
}
a().then(function () {
alert("async")
})
複製程式碼
結語:
通過上述4種方法的對比,我們可以看出
JavaScript
這門語言的發展和進步;ES6+
增加了很多實用功能和方法,將有助於前期程式碼的編寫以及後期程式碼的維護,是時候用async/await
來處理我們的非同步操作了。
案例原始碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.container .ball {
width: 100px;
height: 100px;
border-radius: 50%;
position: absolute;
}
.container .ball:nth-child(1) {
background-color: blue;
left: 0;
top: 20px;
}
.container .ball:nth-child(2) {
background-color: yellow;
left: 200px;
top: 150px;
}
.container .ball:nth-child(3) {
background-color: green;
left: 400px;
top: 280px;
}
</style>
</head>
<body>
<div class="container">
<div class="ball"></div>
<div class="ball"></div>
<div class="ball"></div>
</div>
<!-- <script src="Promise.js"></script> -->
<script>
let ball = document.querySelectorAll(".ball");
let [ball1, ball2, ball3] = [...ball];
// 1.回撥函式處理;
function move(ele, target, callback) {
let left = parseInt(getComputedStyle(ele)["left"]);
let timer = setInterval(function () {
if (left >= target) {
clearInterval(timer);
callback();
} else {
left += 2;
ele.style.left = left + "px";
}
}, 4)
}
move(ball1, 200, function () {
move(ball2, 400, function () {
move(ball3, 600, function () {
alert("callback");
});
})
})
// 2.promise
// generator、async都是基於promise的發展;
// function move(ele, target) {
// return new Promise(function (resolve, reject) {
// let left = parseInt(getComputedStyle(ele)["left"]);
// let timer = setInterval(function () {
// if (left >= target) {
// clearInterval(timer);
// resolve();
// } else {
// left += 2;
// ele.style.left = left + "px";
// }
// }, 4)
// })
// }
// move(ball1, 200).then(function () {
// return move(ball2, 400);
// }).then(function () {
// return move(ball3, 600);
// }).then(function () {
// alert("promise");
// })
// 3.Generator
// function* m() {
// yield move(ball1, 200);
// yield move(ball2, 400);
// yield move(ball3, 600);
// }
// // 利用co方法自動迭代generator
// function co(it) {
// return new Promise(function (resolve, reject) {
// function next(d) {
// let { value, done } = it.next(d);
// if (!done) {
// value.then(function (data) { // 2,txt
// next(data)
// }, reject)
// } else {
// resolve(value);
// }
// }
// next();
// });
// }
// co(m()).then(function () {
// alert("generator");
// })
// 4.async/await
// async function a() {
// await move(ball1, 200);
// await move(ball2, 400);
// await move(ball3, 600);
// }
// a().then(function () {
// alert("async")
// })
</script>
</body>
</html>
複製程式碼