通過一道筆試題淺談javascript中的promise對象


因為前幾天做了一個promise對象捕獲錯誤的面試題目,所以這幾天又重溫了一下promise對象。現在借這道題來分享下一些很基礎的知識點。

下面是一個面試題目,三個promise對象捕獲錯誤的例子,返回結果有什么不同。

 

//使用throw添加錯誤事件
var p = new Promise(function(resolve, reject) { resolve("ok"); throw new Error('error0'); //setTimeout(function() { throw new Error('error1') }, 0);
}); p.then(function(value){ console.log(value)  }) .catch(funcrion(err){ console.log(err) });
process.on('unhandledRejection', function (err, p) { console.error('catch exception:',err.stack) });
//設置定時器來拋出錯誤事件
var p = new Promise(function(resolve, reject) { resolve("ok"); //throw new Error('error0');
    setTimeout(function() { throw new Error('error1') }, 0); }); 
p.then(function(value){
    console.log(value) 
 }) .catch(funcrion(err){ console.log(err) })
;
process.on('unhandledRejection', function (err, p) { console.error('catch exception:',err.stack) });
//同時添加錯誤事件
var p = new Promise(function(resolve, reject) { resolve("ok"); throw new Error('error0'); setTimeout(function() { throw new Error('error1') }, 0); }); p.then(function(value){ console.log(value)  }) .catch(funcrion(err){ console.log(err) });
process.on('unhandledRejection', function (err, p) { console.error('catch exception:',err.stack) });

 

先把問題放在這里,如果一眼能看出結果的大大們就不用再往下面讀了。。

 

大概在很早以前就有了解過javascript中實現異步編程的四種方式。分別是1.回調函數 2.事件監聽 3.發布、訂閱事件 4.promise對象

前三種我們可以說是屢見不鮮了,回調函數,事件監聽這算是js的“靈魂”了。。發布、訂閱事件也是比較常見的。今天我們就來淺顯學習下promise對象,以及它能實現異步編程的原理,最后是上面那個題目的答案以及我個人的一些理解。

一、什么是promise對象,它能干什么?

Promises對象是在CommonJS工作組提出的一種規范,目的是為異步編程提供統一接口。現已在ECMAScript2015(ES6)中實現。

Promise 對象用於延遲(deferred) 計算和異步(asynchronous ) 計算.。一個Promise對象代表着一個還未完成,但預期將來會完成的操作。

Promise 對象是一個返回值的代理,這個返回值在promise對象創建時未必已知。它允許你為異步操作的成功或失敗指定處理方法。 這使得異步方法可以像同步方法那樣返回值:異步方法會返回一個包含了原返回值的 promise 對象來替代原返回值。

 

Promise對象有以下幾種狀態:

pending: 表示一個初始狀態, 非 fulfilled 或 rejected。

fulfilled: 成功的操作。

rejected: 失敗的操作。

每一個異步任務都會返回一個Promise對象,該對象有一個then方法,允許指定回調函數。可以根據Promise對象的狀態相應的去執行對應的回調函數。我們大概了解了promise存在的意義,下面我們具體去看一下該對象常用的幾個API。

二、常用的API

1.Promise.prototype.then()

promise實例具有then方法,因此then方法是定義在原型對象promise.prototype上的。它的作用是為promise實例添加狀態改變時的回調函數。

then()方法的第一個參數是resolved狀態的回調函數,第二個參數(可選)是rejected狀態的回調函數。then方法返回的是一個新的promise實例,非原來的那個promise實例,因此可以采用鏈式寫法,then方法之后還可調用一個then方法。

var p=new Promise(function(resolve,eject){ resolve("ok"); }); p.then(function(value){console.log(val)}, function(err)(console.log(err)) );

then方法的第二個參數一般不推薦寫。有以下兩個原因:第一個原因,由於是鏈式操作,這個then方法之后還可能會有其他操作,如果此時把錯誤捕捉的函數放在后面方法前邊的話,並且之后再無錯誤捕獲方法,then之后的錯誤就會捕捉不到。第二個原因是在then方法里面,兩個參數都是回調函數寫了一大堆,這樣結構看起來比較混亂。

所以下面就有了這個方法,一般寫在鏈式寫法的最后。這樣就可以捕獲到前面所有的錯誤。

2.Promise.prototype.catch()

這個方法是.then(null,rejection)的別名,這也能看出這個方法是專門只能用來捕獲錯誤信息,用於指定發生錯誤時的回調函數。

但是使用這個這個方法的時候要注意一下幾點:

(1)當promise狀態已經變成resolved的時候,再拋出錯誤時是無效的。看下面的代碼。

var promise=new promise(function(resolve,reject){ resolve("ok"); throw new Error("test"); }); promise.then(function(value){consloe.log(val); }) .catch(function(error){console.log(err)});

promise狀態在resolve("ok");之后就會把promise的狀態變為resolved,之后拋出錯誤也不會把promise狀態變為rejected,所以catch方法並不會捕獲到錯誤。

Promise 對象的狀態改變,只有兩種可能:從 Pending 變為 Resolved 和從 Pending 變為 Rejected。只要這兩種情況有任意一種發生,狀態就相當於凝固了,不會再變了,會一直保持這個結果。

 (2)盡量將catch方法寫在鏈式操作的最后,原因上面都已經說過了,也正是捕獲錯誤不推薦寫then方法的原因之一。錯誤會一直冒泡到最后,catch放在最后會捕捉到所有錯誤。當catch設置的過早,並且之后在沒有catch方法的話,那么這個catch之后發生的錯誤不會被捕獲到。

(3)當沒有使用catch方法指定錯誤處理函數的回調函數時,promise對象里面拋出的錯誤不會傳遞到外層的代碼。

3.Promise.resolve()

這個方法的作用就是將現有的對象轉化為Promise對象,進而可以執行這些方法。

Promise.resolve("foo"); //這就相當於下面這種寫法

new Promise(function(resolve){ resolve("foo"); });

 

4.Promise.all()

這個方法用於將多個promise實例,包裝成一個新的promise實例。

 

var p=Promise.all([p1,p2,p3]);

 

p1,p2,p3都是promise對象的實例,如果不是的話,則會調用Promise.resolve()方法,將參數轉化為Promise實例,之后再繼續進行進一步的處理。

並且要注意一下兩點:

(1)只有當p1,p2,p3狀態都變為fulfilled之后,p的狀態才會變為fulfilled。

(2)只要p1.p2,p3中有任意一個狀態變為rejected,p的狀態就會變為rejected。

三、實現異步編程的原理

大概原理就是正如它們所說Promise對象相當於是一個狀態機,在其內部使用resolve方法,使其由初始狀態變為成功時的fulfilled狀態或者執行失敗后的rejected狀態。這時內部的工作就完成了,開始由外部監聽其內部的狀態的改變,調用then()方法(catch()方法相當於then內部的第二個參數方法)對應的狀態調用對應的處理函數。這樣就大概是Promise對象實現異步編程的原理。

四、開頭所給問題的答案

我們可以看到三個實例外部函數的寫法一模一樣,不同的是Promise對象內部拋出錯誤所使用的方法。

第一個使用throw拋出錯誤,理應被外部的catch方法所捕獲到。但是但是。。之前已經說過Promise對象狀態一旦被改變之后就“凝固”了,一旦執行

resolve("ok");

 

狀態被設定為fulfilled之后,再進行拋出錯誤處理,錯誤也不會被后續的catch方法捕獲到。所以這里只會去執行then()方法里面的內容。即只會打印出 “ok”。

第二個通過定時器拋出一個錯誤。這里雖然狀態已經變為fulfilled,但是定時器拋出的錯誤屬於異步拋出的錯誤,無法被try catch捕獲到,因此和Promise對象無關,所以錯誤可以正常的拋出來,所以這里的答案應該是先打印出“ok”,之后拋出process里面定義的錯誤。

第三個同時使用throw和定時器拋出錯誤。是不是里應當和第二個執行情況一樣嗎?這樣想就錯了。當執行throw之后,雖然錯誤未被外部函數捕獲處理,但這也是個實實在在存在的錯誤啊,對於javascript來說,有錯就不會繼續往下面執行了。所以並不會執行到定時器拋出錯誤就停止了。因此這個問題的答案應該是 只打印出“ok”。

 

嗯,結束。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM