火於非同步 1995年,當時最流行的瀏覽器——網景中開始運行 JavaScript (最初稱為 LiveScript)。 1996年,微軟發佈了 JScript 相容 JavaScript。隨著網景、微軟競爭而不斷的技術更新,在 2000年前後,JavaScript 相關的技術基礎準備就緒。 隨後到 ...
火於非同步
1995年,當時最流行的瀏覽器——網景中開始運行 JavaScript (最初稱為 LiveScript)。 1996年,微軟發佈了 JScript 相容 JavaScript。隨著網景、微軟競爭而不斷的技術更新,在 2000年前後,JavaScript 相關的技術基礎準備就緒。 隨後到 2005 年前後,以 Google 為首開始重視使用 AJAX(即 Asynchronous JavaScript and XML),使得複雜的網頁交互體驗接近桌面應用。
然後,隨著 Web 應用變得越來越複雜 ,JavaScript 的生態和重要性也日益提升,YUI、prototype.js、jQuery 等各種庫相應登場,隨之而來就到了 JavaScript 的繁榮期。
2008年,Google 發佈了 JavaScript 引擎 V8 大大改善了 JavaScript 的執行速度,進一步推動了 JavaScript 的繁榮,也為 JavaScript 進軍伺服器端打下了基礎(如:Node.js)。
順序執行非同步函數
非同步為 JavaScript 帶來非阻塞等優勢的同時,同時也在一些場景下帶了不便,如:順序執行非同步函數,下麵總結了一些常用的方法。
1. "回調地獄"
隨著應用複雜度幾何式增加,我們可能遇到下麵“回調地獄”式的代碼。
// 第一個任務
function task1 (callback) {
setTimeout(() => {
console.log('1', '我是第一個任務,必須第一個執行');
callback && callback(1);
}, 3000);
}
// 第二個任務
function task2 (callback) {
setTimeout(() => {
console.log('2', '我是第二個任務');
callback && callback(2);
}, 1000);
}
// 第三個任務
function task3 (callback) {
setTimeout(() => {
console.log('3', '我是第三個任務');
callback && callback(3);
}, 1000);
}
// 所有任務
function allTasks () {
task1((cb1) => {
if (cb1) {
task2((cb2) => {
if (cb2) {
task3((cb3) => {
if (cb3) {
// 順序完成所有任務
}
})
}
});
}
});
}
allTasks();
/**
* 3秒後
* 1 我是第一個任務,必須第一個執行
* 1秒後
* 2 第二個任務
* 1秒後
* 3 第三個任務
*/
2. Promise
為了避免“回調地獄”帶來的複雜性和不易閱讀,ES6 推出了 Promise。這次實現起來簡單多了,但還存在 Promise 中嵌套多層 Promise 的問題,似乎又回到了類似“回調地獄”的問題上。
new Promise(resolve => {
setTimeout(() => {
console.log('1', '我是第一個任務,必須第一個執行');
resolve(1);
}, 3000);
}).then((val) => {
new Promise(resolve => {
setTimeout(() => {
console.log('2', '我是第二個任務');
resolve(2);
}, 1000);
}).then(val => {
setTimeout(() => {
console.log('3', '我是第三個任務');
}, 1000);
});
});
/**
* 3秒後
* 1 我是第一個任務,必須第一個執行
* 1秒後
* 2 第二個任務
* 1秒後
* 3 第三個任務
*/
3. Await、Async
確保支持,詳細見:https://caniuse.com/#search=async
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Statements/async_function
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/await
為了更易書寫和閱讀來實現順序執行非同步函數,ES2017 新增了 await
和 async
。這次書寫體驗非常的棒,就像寫同步代碼一樣完成了順序執行非同步的需求。
/**
* 第一個任務
*/
function task1 () {
return new Promise(resolve => {
setTimeout(() => {
console.log('1', '我是第一個任務,必須第一個執行');
resolve('done');
}, 3000);
});
}
/**
* 第二個任務
*/
function task2 () {
return new Promise(resolve => {
setTimeout(() => {
console.log('2', '第二個任務');
resolve('done');
}, 1000)
});
}
/**
* 第三個任務
*/
function task3 () {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('3', '第三個任務');
reject('error');
}, 1000);
});
}
/**
* 第四個任務
*/
function task4 () {
return new Promise(resolve => {
setTimeout(() => {
console.log('4', '第四個任務');
resolve('done');
}, 2000);
})
}
/**
* 所有任務
*/
async function allTasks () {
await task1();
await task2();
await task3();
await task4();
}
// 執行任務
allTasks();
/**
* 3秒後
* 1 我是第一個任務,必須第一個執行
* 1秒後
* 2 第二個任務
* 1秒後
* 3 第三個任務
* Uncaught (in promise) error
*/
完整案例
基於 Node.js,通過 Await 、Async、Promise 實現的順序執行非同步,爬取豆瓣電影截圖並按順序一張張下載圖片。
參考
- 《JavaScript編程全解》
- https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/