首頁 >web前端 >js教程 >使用JavaScript實現比非同步更好的解決方法?

使用JavaScript實現比非同步更好的解決方法?

亚连
亚连原創
2018-06-13 11:40:301291瀏覽

這篇文章主要告訴大家了JavaScript體驗非同步更好的解決辦法,有這方面需要的朋友跟著學習參考下吧。

一、非同步解決方案的演化史
JavaScript的非同步操作一直是個麻煩事,所以不斷有人提出它的各種解決方案。可以追溯到最早的回呼函數(ajax老朋友),到Promise(不算新的朋友),再到ES6的Generator(強勁的朋友)。
幾年前我們可能用過一個比較著名的Async.js,但是它沒有擺脫回呼函數,並且錯誤處理也是按照“回調函數的第一個參數用來傳遞錯誤”這樣一個約定。而眾所皆知的回調地獄仍然是一個比較突出的問題,直到Generator改變了這種非同步風格。
但是ES7的async await的出現(碉堡的新朋友),我們可以輕鬆寫出同步風格的程式碼同時又擁有非同步機制,可以說是目前最簡單,最優雅,最佳的解決方案了。

二、async await語法
async await語法比較簡單,可以認為是Generator的語法糖,比起星號和yield更具有語意化。下面一個簡單的例子表示1秒之後輸出hello world:

function timeout(ms) {
 return new Promise((resolve) => {
  setTimeout(resolve, ms);
 });
}
async function asyncPrint(value, ms) {
 await timeout(ms);
 console.log(value)
}
asyncPrint('hello world', 1000);

await只能用在async函數中,如果用在普通函數就會報錯

await後面跟的是一個Promise物件(當然其它值也可以,但是會包裝成一個立即resolve的Promise,也就沒有意義了)

await會等待Promise的結果返回再繼續執行

await等待的雖然是Promise對象,但是不必寫.then(),直接可以得到回傳值,將上面的程式碼微調,發現回傳值result也是可以輸出hello world:

function timeout(ms) {
 return new Promise((resolve) => {
  setTimeout(_ => {resolve('hello world')}, ms);
 });
}
async function asyncPrint(ms) {
 let result = await timeout(ms);
 console.log(result)
}
asyncPrint(1000);

三、async await錯誤處理

前面說了await等待的雖然是Promise對象,但是不必寫.then(),所以其實也不用寫.catch()了,直接用try catch就能捕捉錯誤,這樣可以避免錯誤處理程式碼非常冗餘餘和笨重,還是將上面的例子微調:

function timeout(ms) {
 return new Promise((resolve, reject) => {
  setTimeout(_ => {reject('error')}, ms);//reject模拟出错,返回error
 });
}
async function asyncPrint(ms) {
 try {
   console.log('start');
   await timeout(ms);//这里返回了错误
   console.log('end');//所以这句代码不会被执行了
 } catch(err) {
   console.log(err); //这里捕捉到错误error
 }
}
asyncPrint(1000);

如果有多個await,可以一起放在try catch中:

async function main() {
 try {
  const async1 = await firstAsync();
  const async2 = await secondAsync();
  const async3 = await thirdAsync();
 }
 catch (err) {
  console.error(err);
 }
}

四、async await注意點

1). 前面已經說過,await指令後面的Promise對象,運行結果很可能是reject或邏輯報錯,所以最好把await放在try catch程式碼區塊中。

2). 多個await指令的非同步操作,如果不存在依賴關係,讓它們同時觸發。

const async1 = await firstAsync();
const async2 = await secondAsync();

上面程式碼中,async1和async2如果是兩個獨立的非同步操作,這樣寫會比較耗時,因為只有firstAsync完成以後,才會執行secondAsync,完全可以用Promise.all優雅地處理:

let [async1, async2] = await Promise.all([firstAsync(), secondAsync()]);

3). await只能用在async函數之中,如果用在普通函數就會報錯:

async function main() {
 let docs = [{}, {}, {}];
 //报错 await is only valid in async function
 docs.forEach(function (doc) {
  await post(doc);
  console.log('main');
 });
}
function post(){
 return new Promise((resolve) => {
  setTimeout(resolve, 1000);
 });
}

在forEach內部方法加上async就可以了:

async function main() {
 let docs = [{}, {}, {}];
 docs.forEach(async function (doc) {
  await post(doc);
  console.log('main');
 });
}
function post(){
 return new Promise((resolve) => {
  setTimeout(resolve, 1000);
 });
}

但你會發現3個main是同時輸出的,這就說明post是並發執行的,而不是繼發執行,改成for就可以解決問題,3個main是分別相隔1秒輸出:

async function main() {
 let docs = [{}, {}, {}];
 for (let doc of docs) {
  await post(doc);
  console.log('main');
 }
}
function post(){
 return new Promise((resolve) => {
  setTimeout(resolve, 1000);
 });
}

總之,用了async await之後整個人神清氣爽,可以用非常簡潔和優雅的代碼實現各種花式異步操作,並且在業務邏輯複雜的情況下可以不用陷入回調地獄中。不敢說這一定是終極的解決方案,但確實是目前最優雅的解決方案!

上面是我整理給大家的,希望今後對大家有幫助。

相關文章:

在Angular中如何整合zTree程式碼

node打包工具Pkg(詳細教學)

在微信中如何使用js喚起App?

以上是使用JavaScript實現比非同步更好的解決方法?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn