1. 程式人生 > >異步代碼

異步代碼

錯誤信息 使用 ole 同時 鏈式 ESS 不同之處 回調 var

一、Promise

  promise表示一個異步操作的最終結果,可以理解為狀態機,它存在三種不同的狀態:

(1)Pending:表示還在執行。

(2)Fulfilled(或者resolved):執行成功。

(3)Rejected:執行失敗。

1、將異步方法封裝成Promise

 1 //Promise的構造函數
 2 var promise = new Promise(function (resolve,reject) {
 3     if(/*異步操作成功*/){
 4         resolve(value);
 5     }else{
 6         reject(value);
7 } 8 }) 9 //使用Promise封裝的readFile 10 var fs = require("fs"); 11 function readFile_promise(path) { 12 return new Promise(function(resolve, reject) { 13 fs.readFile(path, "utf-8", function(err, data) { 14 if (data) { 15 resolve(data); 16 } else {
17 reject(err); 18 } 19 }); 20 }); 21 }

其中,resolve和reject是兩個函數,resolve函數會在異步操作成功完成時被調用,並將異步操作的返回值作為參數傳遞到外部;reject則在異步操作出現異常時被調用,會將錯誤信息作為參數傳遞出去。即resolve和reject方法沒有做多余的操作,僅僅把異步的結果傳遞出去而已,對於異步結果,是交給then方法來完成的。

2、使用then方法獲取結果

 1 //處理結構
 2 promise.then(function (data) {
3 //success 4 },function (error) { 5 //failure 6 }) 7 var pro = readFile_promise(‘foo.txt‘); 8 pro.then(function(value) { 9 console.log(value); 10 return readFile_promise(‘bar.txt‘); 11 }).then(function(value) { 12 console.log(value); 13 }).catch(function(err) { 14 console.log(err); 15 });//捕獲異常

3、Promise的常用API

(1)Promise.resolve:將一個給Promise對象轉化為Promise對象。但resolve不能轉換一個異步方法,例如readFile方法。

 1 var obj = {
 2     then: function() {
 3         console.log("I am a then method");
 4     }
 5 }
 6 Promise.resolve(obj); //轉化後的Promise會自動執行其的then方法
 7 //I am a then method
 8 var p = Promise.resolve("Hello World");
 9 p.then(function (result) {
10     console.log(result);
11 }); //Hello World

(2)Promise.reject:也是返回一個Promise對象,不同之處在於這個Promise的狀態為reject,reject方法的參數會作為錯誤信息傳遞給then方法。

(3)Promise.all:將多個Promise對象包裝成一個Promise。當調用Promise.all時,所有的Promise都已經開始執行了,all方法只是等到全部的Promise完成後,對所有的執行結果做一下包裝再返回。

1 var promises = ["foo.txt", "bar.txt", "baz.txt"].map(function(path) {
2     return readFile_promise(path);
3 });
4 Promise.all(promises).then(function(results) {
5     console.log(results); //results的內容是文本文件內容的順序排列
6 }).catch(function(err) {
7 
8 });

(4)Promise.race:接收一個Promise數組作為參數並返回一個新的Promise,數組中的Promise會同時開始執行,race返回的Promise的狀態由數組中率先執行完畢的Promise的狀態決定。

(5)Promise.catch:Promise在執行中如果出了錯誤,可以使用throw關鍵字拋出錯誤,並且可以使用catch方法進行去捕獲;如果不設置任何回調函數捕獲錯誤,Promise內部拋出的錯誤就無法傳遞到外部。

1 var promise = new Promise(function(resolve, reject) {
2     throw new Error("get error");
3 });
4 //如果不設置catch函數,上面即使拋出error也不會使進程退出。
5 promise.catch(function(error) {
6     console.log(error);
7 });

4、Promise的不足:Promise的一堆鏈式調用會讓人看起來很麻煩。可以考慮將then方法的回調函數抽取出來。

二、Generator

  我們無法控制Promise的執行,新建一個Promise後,其狀態自動轉化為pending,同時開始執行,直到狀態改變後我們才能進行下一步操作。而Generator函數不同,Generator函數可以由用戶執行中斷或者恢復執行的操作,Generator中斷後可以轉去執行別的操作,然後再回頭從中斷的地方恢復執行。

  Generator函數和普通函數在外表上最大的區別有兩個:

(1)在function關鍵字和方法名中間有個星號(*)。

(2)方法體中使用“yield”關鍵字。

 1 var fs = require("fs");
 2 
 3 function readFile_promise(path) {
 4     return new Promise(function(resolve, reject) {
 5         fs.readFile(path, "utf-8", function(err, data) {
 6             if (data) {
 7                 resolve(data);
 8             } else {
 9                 reject(err);
10             }
11         });
12     });
13 }
14 
15 function* gen() {
16     var result = yield readFile_promise(‘../baz.txt‘);
17     console.log(result);
18 }
19 var g = gen();
20 var result = g.next();
21 //console.log(result);
22 result.value.then(function(data) {
23     g.next(data);
24 });

之所以可以使用Generator函數來處理異步任務,原因有二:

(1)Generator函數可以中斷和恢復執行,這個特性有yield關鍵字來實現。

(2)Generator函數內外可以交換數據,這個特性由next函數來實現。

即Generator函數處理異步任務的核心思想:先將函數暫停在某處,然後拿到異步操作的結果,然後再把這個結果傳到方法體內。

三、async

  async函數可以看作是自帶執行器的Generator函數。await關鍵字後面往往是一個Promise,如果不是就隱式調用Promise.resolve來轉換成一個Promise。

 1 var fs = require("fs");
 2 async function asyncFunc() {
 3 
 4     return "hello node";
 5 }
 6 asyncFunc().then(function(data) {
 7     console.log(data);
 8 });
 9 
10 function readFile_promise(path) {
11     return new Promise(function(resolve, reject) {
12         fs.readFile(path, "utf-8", function(err, data) {
13             if (data) {
14                 resolve(data);
15             } else {
16                 reject(err);
17             }
18         });
19     });
20 }
21 
22 aysnc
23 
24 function readFile() {
25     var result = await readFile_promise("../foo.txt");
26     console.log(‘test‘);
27 }
28 readFile();

  async函數的優點:可以實現自動執行,無須借助第三方模塊等,也免去了Generator函數中一些復雜的概念,async函數的聲明和執行與普通同步函數幾乎一模一樣(除了async和await關鍵字外)。

  async函數也有一些不足的地方,如果我們有很多層的方法調用,最底層的異步操作被封裝成了async方法,那麽該函數的所有上層方法可能都要變成async方法。

  

異步代碼