1. 异步JavaScript
在 JS 中,经常要处理一些异步回调,比如从网络获取文件,访问数据库,从网络摄像头获得视频流;
例如下面的代码,因为从服务器上获取一个图像需要时间,下面的代码可能不能正常工作;
var response = fetch('myImage.png');
var blob = response.blob();
// display your image blob in the UI somehow
那么如何解决这类问题呢?方法有很多:可分为异步callbacks(回调函数)和 其他方法(promise、async\await、generator)。下面一一介绍
2. 异步callbacks
以使用 XMLHttpRequest API 为例:
function loadAsset(url, type, callback) {
let xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = type;
xhr.onload = function() {
callback(xhr.response); //把响应作为参数传递给回调函数去处理
};
xhr.send();
}
function displayImage(blob) {
let objectURL = URL.createObjectURL(blob);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
}
loadAsset('coffee.jpg', 'blob', displayImage);
这里我们在loadAsset
函数中发出网络请求,但注意我们向loadAsset函数中传入了一个 回调函数(displayImage
); 当浏览器收到服务器返回来的数据时,会触发onload
事件,并把响应作为参数传递给回调函数去处理。
这样,我们就解决了之前的问题:还未收到响应数据就执行处理函数。
3. Promise
Promise 是专门为处理异步逻辑而设计的,它让异步回调过程更加清晰。让我们看看上面的代码使用Promise改写是怎样的:
function loadAsset(url, type) {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = type;
xhr.onload = function() {
resolve(xhr.response); //返回收到的响应数据
};
xhr.send();
});
}
function displayImage(blob) {
let objectURL = URL.createObjectURL(blob);
let image = document.createElement("img");
image.src = objectURL;
document.body.appendChild(image);
}
//不再需要传入回调函数
loadAsset("coffee.jpg", "blob").then(data => {
displayImage(data);
});
比较后我们会发现,这里不再需要给 loadAsset
传入回调函数,Promise帮我们处理了这个逻辑,在收到响应后,由于我们使用了resolve
返回响应数据,我们直接在then
方法中就能拿到我们要的数据了!
虽然代码看起来多了一点,但是逻辑看起来却更清楚了,特别是遇到 回调函数嵌套 的问题。这就是使用Promise的好处。
4. 生成器(Generator)
让我们看一下 Generator 的示例:
function* say(){
yield "开始";
yield "执行中";
yield "结束";
}
let it = say(); // 调用say方法,得到一个迭代器
console.log(it.next()); // { value: '开始', done: false }
console.log(it.next()); // { value: '执行中', done: false }
console.log(it.next()); // { value: '结束', done: false }
console.log(it.next()); // { value: undefined, done: true }
生成器函数定义时在function关键字后面多加一个*
号。它和普通函数不同,普通函数一次执行完所有代码,但它可以通过yield
关键字将函数的执行挂起,再外部在通过调用next
方法,让函数继续执行,直到遇到下一个yield,或函数执行完毕。
这样,异步操作也可以变得和同步操作一样。
5. async/await
async/await
是常用的处理异步逻辑的方法,它其实就是生成器的语法糖。
async function getData(){
var response = await fetch('myImage.png');
var blob = response.blob();
}
在异步处理前加 await
,这样函数将等待异步流程结束后才执行之后的代码。