[譯]ES6入門(第二部分)

沉默的範大叔發表於2018-10-03

原文戳這裡

[譯]ES6入門(第一部分)

[譯]ES6入門(第三部分)

這篇文章的第一部分出現在這裡。 我在那裡介紹了一些有趣的功能:)

我將在這篇文章中介紹的主題包括:

1、Promises

2、Async / Await

Promises

Promise是ES6中的一個有用功能。它們用於進行非同步操作,例如API請求,檔案處理,下載影像等。

那麼,什麼是非同步? (繼續,如果你已經知道)

非同步操作是需要一些時間才能完成的操作。

例如,假設您正在定義向伺服器發出API請求的函式。該函式不會立即返回結果,因為從伺服器獲取響應需要幾秒鐘。

因此,如果您正在呼叫該函式並將其值(即)返回的結果分配給某個變數,那麼它將是未定義的。因為Javascript不知道該函式正在處理一些非同步操作。

那我們該如何處理呢?

讓我們先談談一些歷史。

在Promise之前,程式設計師習慣於定義回撥。回撥是Javascript中的常規函式,它在非同步操作完成時執行。

例如,您定義了一個向伺服器發出API請求的函式。然後你設定了一個回撥函式,它將在我們從伺服器獲得響應時執行。

所以在上面的例子中,Javascript不會停止執行,直到我們從API獲得響應。我們已經定義了一個函式(回撥),它將在我們得到響應後執行。我想你明白了。

那麼,Promise是什麼?

Promise是有助於執行非同步操作的物件。

從技術上講,它們是表示非同步操作完成的物件。 (如果你不明白,請繼續往下閱讀一會兒)

在解釋如何定義Promise之前,我將解釋Promise的生命週期。

在Promise中有三種狀態:

1、掛起:在這種狀態下,promise只是執行非同步操作。例如,它正在向伺服器發出一些API請求或從cdn下載一些影像。 從這個狀態Promise可以轉移到完成狀態或拒絕狀態

2、完成:如果Promise已達到此狀態,則表示非同步操作已完成,並且得到了輸出的結果。例如,我們有了來自API的響應。

3、拒絕:如果Promise已達到此狀態,則表示非同步操作未成功,並且我們得到導致操作失敗的錯誤。

好的..讓我們看一些程式碼。

const apiCall = new Promise(function(resolve, reject) {
 // 非同步操作在這裡定義..
});
複製程式碼

通過使用new關鍵字建立建構函式來定義Promise。 然後建構函式將有一個函式(我們稱之為執行函式。)

非同步操作在執行函式內定義。

請注意,執行函式有兩個引數resolve和reject。

第一個引數resolve實際上是一個函式。 它在執行函式內部呼叫,它表示非同步操作成功,我們得到了結果。 resolve函式有助於Promise從掛起狀態轉為完成狀態。 希望你明白了。:)

像resolve一樣,reject也是一個函式。 它也在執行函式內部呼叫,它表示非同步操作不成功,我們遇到了錯誤。 reject函式有助於Promise從掛起狀態轉為拒絕狀態。:)

const apiCall = new Promise(function(resolve, reject) {
 if ( API request to get some data ) {
  resolve("The request is successful and the response is "+ response);
 }
 else {
  reject("The request is not successful. The error is "+error);
 }
});
複製程式碼

在上面的程式碼中,您可以看到我們在執行函式中完成了一些非同步操作。 如果我們從伺服器獲得響應,則呼叫resolve函式。 如果有錯誤,則會使用錯誤訊息呼叫reject函式。

我們已經完成了Promise。 讓我們看看如何執行Promise並處理輸出。

// 呼叫promise.
apiCall
複製程式碼

就是這樣。 結束了。 :) :)

開玩笑。 還沒結束。

在上面的程式碼中,呼叫函式並執行promise(即執行函式)。 然後根據輸出呼叫resolve或reject函式。

但是你可以看到我們沒有處理promise中返回的輸出。

例如,如果我們從API獲得響應,那麼我們必須處理響應。 或者如果我們得到錯誤,我們需要正確處理它。

那我們該如何處理呢?

我們使用處理器來獲取promise的輸出。

處理器是在某些事件發生時執行的函式,例如單擊按鈕,移動游標等。

因此,當resolve函式被呼叫或reject函式被呼叫時,我們可以使用處理器來處理。

簡單。:)

我們來看一些程式碼

// 呼叫promise,並使用handlers.
apiCall.then(function(x) {console.log(x); })
複製程式碼

在上面的程式碼中,我們將一個處理器then附加到promise。處理器then有一個函式引數。函式引數本身有一個引數x。

發生了什麼事?

當在promise內呼叫resolve函式時,處理器then執行其函式引數。

我會再解釋一次。

處理器then監聽resolve函式被呼叫時的事件。 因此,當呼叫resolve函式時,處理器then執行其函式引數。

apiCall.then(function(x) {console.log(x); })
// 輸出
The request is successful and the response is {name: "Jon Snow"}
複製程式碼

同樣,還有另一個處理器catch。

Catch監聽reject函式。

Catch函式在reject函式被呼叫時執行其函式引數。

apiCall.then(function(x) {console.log(x); }).catch(function(x) {console.log(x); })
// 假設這個請求沒有成功 (在promise中reject函式被呼叫. )
輸出:
The request is not successful
複製程式碼

我想你明白了。

上面的程式碼不太可讀。 所以讓我們嘗試重構它。

apiCall
.then(function(x) {
 console.log(x); 
})
.catch(function(x) {
 console.log(x);
}) 
複製程式碼

啊......現在可讀了。 大多數程式設計師都這樣寫。

好吧..所以我覺得你已經走了很長的路。

我們來回顧一下。

1、使用帶有函式引數的new關鍵字定義Promise。 然後函式本身有兩個函式引數resolve和reject。

2、操作成功時應呼叫resolve函式。

3、操作失敗時應呼叫reject函式。

4、處理器then監控resolve函式。

5、處理器catch監控reject函式。

6、確保程式碼的可讀性:) :)

這是可執行的示例。 如果您不熟悉,請練習。

var ApiCall = new Promise(function(resolve, reject) {
  
  var request = new XMLHttpRequest();
  request.open('GET', 'https://api.github.com/users/srebalaji');

  request.onload = function() {
    if (request.status == 200) {
      
      resolve(request.response);
    } else {
      reject(Error(request.statusText));
    }
  }

  request.send();
});

ApiCall
.then(function(x) {
  document.getElementById('response').innerHTML = x;
})
.catch(function(x) {
	document.getElementById('response').innerHTML = x;
})
複製程式碼

希望你能理解這個例子。 它直截了當。

Async / Await

如果您瞭解Promises,那麼Async / Await非常簡單。 如果你沒有弄明白Promises,Async / Await可以幫助你理解它。 也許你也可以徹底地躲開Promises。:)

Async

Async關鍵字使任何函式只返回promises。

例如,請看以下程式碼

async function hello() {
 return "Hello Promise..!"
}
複製程式碼

函式hello將返回一個promise。

上面的程式碼相當於下面的程式碼。

function hello() {
 return new Promise(function(resolve, reject) {
 // executor function body
 });
}
複製程式碼

非常簡單是吧?

另外一個例子:

async function hello(a, b) {
 if (a < b) {
  return "Greater";
 }
 else {
  return new Error("Not Greater");
 }
}
hello(14, 10)
.then(function(x) {
 console.log("Good..! " + x); 
})
.catch(function(x) {
 console.log("Oops..! " + x); 
})
輸出:
Oops..! Not Greater. 
// 如果你呼叫 hello(4, 10) 你會得到 "Good..! Greater"
複製程式碼

在上面的程式碼中,我們定義了一個async函式並返回了一些值或返回了一些錯誤。

如果要在async函式中返回一些值,則它等同於呼叫resolve函式。

如果通過使用'new'呼叫錯誤建構函式(即)返回一些錯誤,則它等同於reject函式。

不要忘記async函式將返回一個promise。 當然,你也可以在async函式中呼叫resolve和reject函式。

讓我們看看它是如何工作的。

async function Max(a, b) {
 if (a > b) {
  return Promise.resolve("Success");
 }
 else {
  return Promise.reject("Error");
 }
}
Max(4, 10)
.then(function(x) {
 console.log("Good " + x); 
})
.catch(function(x) {
 console.log("Oops " + x); 
});
輸出:
Oops Error
// 如果呼叫 Max(14, 10) 我們會得到 "Good Success" :)
複製程式碼

Await

顧名思義,它使Javascript等到操作完成。 假設您正在使用await關鍵字發出API請求。 它使Javascript等待,直到您從端點獲得響應。 然後它恢復執行。

好的..讓我們走得更遠

Await只能在非同步函式中使用。 它在非同步功能之外不起作用

我們來看一個例子吧

async function hello() {
 let response = await fetch('https://api.github.com/');
 // 上面一行從給定的API終端獲取響應
 return response;
}
hello()
.then(function(x) {
 console.log(x); 
});
...
...
輸出:
Response from the API.
複製程式碼

在上面的程式碼中,您可以看到我們在從API獲取響應時使用了await。

獲取操作可能需要幾秒鐘才能獲得響應,直到獲取到響應,執行將暫停並稍後恢復。

請注意,await操作僅停止hello函式內的執行。 hello函式外的其餘程式碼不會受到影響。 執行在函式外部繼續。 當我們得到響應時,執行內部函式引數。

希望你明白了。

我們來看一個例子

function getResponse() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve("Response from API. Executed after 5 secs");
    }, 5000);
  });
}

async function hello() {
  let response = await getResponse();
  return response;
}

hello()
  .then(function(x) {
    console.log(x);
  })
  .catch(function(x) {
    console.log(x);
  });
console.log("Im executed before getResponse()");
複製程式碼

在上面的示例中,您可以看到我們對getResponse函式使用了await。 並且getResponse將在5秒後返回輸出或錯誤。 因此,在此之前,執行將暫停,然後返回響應。

讓我們看一些實時的例子

function getResponse(url) {
	return new Promise(function(success, failure) {
  var request = new XMLHttpRequest();
	request.open('GET', url);
	
  request.onload = function() {
    if (request.status == 200) {
      return success(request.response);
    } else {
      return failure("Error in processing..!" + request.status);
    }
  }
  request.onerror = function() {
    return failure("Error in processing ");
  }
  request.send();
  });
}

function getUsername(response) {
  response = JSON.parse(response);
  return response["login"];
}

function makeUsernameCaps(name) {
	return new Promise(function(success, failure) {
  	// Let's assume it takes 3secs to make the username caps :) 
  	setTimeout(function() {
    success(name.toUpperCase());
    }, 3000)
  });
}
async function apiCall(url) {
  let response = await getResponse(url); 
  let username = await getUsername(response);
  let username_in_caps = await makeUsernameCaps(username);
  return username_in_caps;
}

apiCall("https://api.github.com/users/srebalaji")
.then(function(x) {
	console.log(x);
})
.catch(function(x) {
	console.log("Error - "+x);
});
複製程式碼

在上面的例子中,您可以看到我們已經使用了多個await。 因此,對於每個await, 程式停止執行,直到收到響應然後恢復。

在示例中嘗試使用一些無效網址。 您可以看到引發錯誤。

async函式中的錯誤處理非常簡單。 如果在async函式內引發錯誤,或者在async函式中使用await呼叫的其他函式內引發錯誤,則會呼叫reject函式。 簡單。

相關文章