ES6 - Note5:Promise

来源:http://www.cnblogs.com/zmxmumu/archive/2016/06/17/5591827.html
-Advertisement-
Play Games

1.Promise介紹 Promise最早是社區提出和實現,後面ES6將其寫入標準,並原生提供Promise對象,是一種非同步編程的解決方案,具體的概念大家可以去查看相關的資料。傳統上處理非同步都是以callback回調函數的方式完成,但是當回調嵌套的太多,便會使程式很難理解,如下所示 如果後面還有基於 ...


1.Promise介紹

Promise最早是社區提出和實現,後面ES6將其寫入標準,並原生提供Promise對象,是一種非同步編程的解決方案,具體的概念大家可以去查看相關的資料。傳統上處理非同步都是以callback回調函數的方式完成,但是當回調嵌套的太多,便會使程式很難理解,如下所示

function a(cb){
    console.log('a...');
    cb('a');
}
function b(cb){
    console.log('b...');
    cb('b');
}
function c(cb){
    console.log('c...');
    cb('c');
}
a(() => b( () => c( () => {}) ));
a...
b...
c...

如果後面還有基於C函數的輸出結果的邏輯,回調將是很大的困惱。

而使用Promise來處理這種非同步回調將會非常的直觀,Promise以同步的操作完成非同步的處理,如下所示

function a(){
    console.log('a...');
    return 'a output';
}
function b(res){
    console.log('b...');
    console.log('get A:'+res);
        return "b output";
}
function c(res){
    console.log('c...');
    console.log('get B:'+res);
}
new Promise((resolve,reject) => {setTimeout(resolve,1000)}).then(a).then(b).then(c);
Promise { <state>: "pending" }
a...
b...
get A:a output
c...
get B:b output

2.Promise的用法

Promise是一個構造函數,接收一個函數參數,該函數接收兩個函數作為參數,分別是resolve,reject。這兩個函數由Promise自己提供,無需自己部署。resolve函數將Promise的狀態從'Pending'轉為'Resolved',reject函數將狀態從'Pending'轉為'rejected'。

基本語法,如下所示

new Promise((resolve,reject) => {
    if(success){ //非同步處理成功
        resolve();
    }else{
        reject();
    }
}).then(onFulfilled,onRejected);

創建一個Promise實例,如下所示

var promise = new Promise((resolve,reject) => {console.log('hehe');resolve('resolved')});
promise.then((val) => console.log(val))
console.log('current');
hehe
current
resolved

resolve函數與reject函數調用時可以傳遞參數,一般給reject函數傳遞Error的實例,用於指出拋出的錯誤,傳給reject函數的參數可以是一般的數值,也可以是Promise的實例,這時該Promise的實例狀態由參數Promise實例的狀態決定,如下所示

var p1 = new Promise((resolve,reject) => {
    setTimeout(()=>{console.log('1秒後的 p1');resolve(p2);},1000);
});
var p2 = new Promise((resolve,reject)=>{
    setTimeout(()=>{console.log('2秒後的 p2');resolve('p2');},2000);
});

p1.then((val)=>console.log(val));
Promise { <state>: "pending" }
1秒後的 p1
2秒後的 p2
p2

如上所示,Promise是創建就立即執行,且then方法的回調函數將在當前腳本所有同步語句執行完執行,但是比定時器的隊列優先順序要高,如下所示

setTimeout(()=>console.log('settimeouting...'),0);
var promise = new Promise((resolve,reject) => {
    console.log('promise constructing...');
    reject(new Error('error'));
});
promise.then(null,(reason)=>console.log(reason));
promise constructing...
Promise { <state>: "pending" }
Error: error
堆棧跟蹤:
promise<@debugger eval code:4:9
@debugger eval code:2:15

settimeouting...

Promise的原型方法then,該方法為promise實例添加狀態改變時的回調函數,第一個參數是狀態為resolved時的回調,第二個參數是狀態為rejected的回調。如下所示

var p3 = new Promise((resolve,reject) => {
    console.log('p3');
    resolve('p3');
});
var p4 = new Promise((resolve,reject) => {
    console.log('p4');
    setTimeout(()=>resolve('p4'),1500);
});
p3.then(()=>p4).then((result) => console.log(result));//這裡需要註意一點,如果then方法返回一個Promise實例,則下一個then回調需要等待該Promise實例的狀態改變才會執行。
p3
p4
Promise { <state>: "pending" }
p4

Promise內部拋出的錯誤不會被捕獲(Chrome瀏覽器除外),除非使用catch方法,如果在resolve之前拋出,則resolve不會執行,相反則相當於沒有拋出錯誤,如下所示

var promise = new Promise((resolve,reject) => {
    console.log('promise constructing...');
    throw new Error('error');
    resolve('haha');
});
promise.then((val)=>console.log(val));
promise constructing...
-------------------------------------------------------------
var promise = new Promise((resolve,reject) => {
    console.log('promise constructing...');
        resolve('haha');
    throw new Error('error');
    
});
promise.then((val)=>console.log(val));
promise constructing...
haha

使用Promise的原型方法catch捕獲錯誤,該方法其實是.then(null,reject)的別名,如下所示

var promise = new Promise((resolve,reject) => {
    console.log('promise constructing...');
    throw new Error('error');
    resolve('haha');
});
promise.then((val)=>console.log(val)).catch((error)=>console.log(error));

promise constructing...
Promise { <state>: "pending" }
Error: error
堆棧跟蹤:
promise<@debugger eval code:3:8
@debugger eval code:1:19

 Promise的錯誤捕獲可以使用then方法的第二個參數指定reject的回調函數,也可以使用Promise.prototype.catch方法捕獲,但是一般推薦使用方法二,因為catch不僅能捕獲到promise內部拋出的錯誤,then方法中的錯誤也能被捕獲。但是在調用resolve方法之後,再拋出錯誤,不會被捕獲,相當於沒有拋出錯誤,如下所示

var promise = new Promise((resolve,reject) => {
    throw new Error('promise error');
});
promise.then(null,(err)=>console.log(err));
Promise { <state>: "pending" }
Error: promise error
堆棧跟蹤:
promise<@debugger eval code:2:9
@debugger eval code:1:19
---------------------------catch method-------------------
var promise = new Promise((resolve,reject) => {
    reject(new Error('promise error'));
});
promise.catch((error)=>console.log(error));
Promise { <state>: "pending" }
Error: promise error
堆棧跟蹤:
promise<@debugger eval code:2:9
@debugger eval code:1:19
---------------------------then方法中報錯-------------------
var p5 = new Promise((resolve,reject) => {
    resolve('hehe');
});
p5.then((val)=>val+x).catch((err)=>console.log(err));
Promise { <state>: "pending" }
ReferenceError: x is not defined
堆棧跟蹤:
@debugger eval code:4:16

catch方法也是返回一個Promise,因此在catch後面還可以接著使用鏈式方法then,但這時then方法報錯將不會被前面的catch捕獲,如下所示

var p5 = new Promise((resolve,reject) => {
    resolve('hehe');
});
p5.then((val)=>val+x).catch((err)=>console.log(err)).then((val)=>y+3);
Promise { <state>: "pending" }
ReferenceError: x is not defined
堆棧跟蹤:
@debugger eval code:4:16

3.Promise.all

Promise.all方法可以將多個Promise實例[p1,p2,p3...]包裝成一個Promise對象A,A的狀態由p1,p2,p3...決定:如果p1,p2,p3...的狀態都變為onfulfilled,A的狀態才變為onfulfilled,且p1,p2,p3...的非同步操作結果組成一個數組傳給回調函數;但凡p1,p2,p3...中有一個狀態變為了rejected,A的狀態立即變為rejected,且第一個rejected的返回值會傳給回調函數,如下所示

var p1 = new Promise((resolve,reject) => {
    console.log('p1...');
    setTimeout(()=>resolve('p1 resolved...'),1000);
});
var p2 = new Promise((resolve,reject) => {
    console.log('p2...');
    setTimeout(()=>resolve('p2 resolved...'),2000);
});
var p3 = new Promise((resolve,reject) => {
    console.log('p3...');
    setTimeout(()=>resolve('p3 resolved...'),3000);
});

Promise.all([p1,p2,p3]).then((args)=>{console.log('3S後的輸出:');for(let v of args){console.log(v);}});
p1...
p2...
p3...
3S後的輸出:
p1 resolved...
p2 resolved...
p3 resolved...
------------------------  有一個Promise實例變為rejected ---------------
var p1 = new Promise((resolve,reject) => {
    console.log('p1...');
    setTimeout(()=>resolve('p1 resolved...'),1000);
});
var p2 = new Promise((resolve,reject) => {
    console.log('p2...');
    setTimeout(()=>resolve('p2 resolved...'),2000);
});
var p3 = new Promise((resolve,reject) => {
    console.log('p3...');
    setTimeout(()=>resolve('p3 resolved...'),3000);
});
var p4 = new Promise((resolve,reject) => {
    console.log('p4...');
    reject("p4 rejected...");
});
Promise.all([p1,p2,p3,p4]).then((args)=>console.log(args),(reason) => console.log(reason));
p1...
p2...
p3...
p4...
Promise { <state>: "pending" }
p4 rejected...

4.Promise.race

同Promise.all一樣,該方法也是將多個Promise實例[p1,p2,p3...]包裝成一個新的Promise實例B。"race"的字面意思就是競爭,所以[p1,p2,p3...]誰的狀態先改變,B的狀態立即改變一樣的狀態且第一個改變狀態的promise實例的返回值傳遞給回調函數,如下所示

var p1 = new Promise((resolve,reject) => {
    console.log('p1...');
    setTimeout(()=>resolve('p1 resolved...'),1000);
});
var p2 = new Promise((resolve,reject) => {
    console.log('p2...');
    setTimeout(()=>resolve('p2 resolved...'),2000);
});
var p3 = new Promise((resolve,reject) => {
    console.log('p3...');
    setTimeout(()=>resolve('p3 resolved...'),3000);
});

Promise.race([p1,p2,p3]).then((val)=>console.log(val),(reason)=>console.log(reason));

p1...
p2...
p3...
Promise { <state>: "pending" }
p1 resolved...

5.Promise.resolve與Promise.reject

Promise.resolve是將給定的參數轉換成一個Promise對象,如下所示

Promise.resolve(arg);//等同於new Promise((resolve)=>resolve(arg))
omise.resolve('resolve convert...').then((val)=>console.log(val));
Promise { <state>: "pending" }
resolve convert...

根據給定的參數類型不同,Promise.resolve有不同的動作:

A、參數為Promise實例,則直接返回該實例

B、參數為空,執行返回一個resolved狀態的新Promise實例

C、參數為普通的數值或對象,則直接返回一個resolved狀態的新Promise實例,如下所示

Promise.resolve({x:1}).then((val)=>console.log(val));
Promise { <state>: "pending" }
Object { x: 1 }

D、參數為thenable對象,既對象實現了then方法,此時返回一個新Promise實例並立即執行對象then方法,如下所示

var thenableObj = {
    then(resolve,reject){
        reject('test...');
    }
};
Promise.resolve(thenableObj).then(null,(reason)=>console.log(reason));

Promise { <state>: "pending" }
test...

Promise.reject與Promise.resolve的用法一致,只是返回的Promise實例的狀態為rejected,如下所示

Promise.reject('reject convert...').then(null,(reason) => console.log(reason));
Promise { <state>: "pending" }
reject convert...

6.給原生Promise對象擴展方法,如下所示

//一下兩個函數來自raunyifeng博客
Promise.prototype.done = function (onFulfilled, onRejected) {
  this.then(onFulfilled, onRejected)
    .catch(function (reason) {
      setTimeout(() => { throw reason }, 0);
    });
};
該方法只要執行就可以捕獲前面調用鏈中的錯誤
Promise.prototype.finally = function (callback) {
  let P = this.constructor;
  return this.then(
    value  => P.resolve(callback()).then(() => value),
    reason => P.resolve(callback()).then(() => { throw reason })
  );
};
該方法不管Promise的實例的狀態是否改變都會執行回調函數

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 最近在開發過程中,遇到了一個場景,甚是棘手,在這裡分享一下。希望大家腦洞大開一起來想一下解決思路。鄙人也想了一個方案拿出來和大家一起探討一下是否合理。 一、簡單介紹一下涉及的對象概念 工作單元:維護變化的對象列表,在整塊業務邏輯處理完全之後一次性寫入到資料庫中。 領域事件:領域對象本身發生某些變化時 ...
  • jsp主要就是用之前的jsonArray讀取 ajax可以設置個定時器,然後設置的方法,至於方法是今天學習到的 function ajax(method,data,url,success){ var xhr=null; try{ xhr=new XMLHttpRequest("Microsoft.X ...
  • 背景:angular與jquery類庫的協作 第三方類庫中,不得不提的是大名鼎鼎的jquery,現在基本上已經是國內web開發的必修工具了。它靈活的dom操作,讓很多web開發人員欲罷不能。再加上已經很成熟的jquery UI 庫和大量jquery 插件,幾乎是一個取之不盡用之不竭的寶庫。然而,它是 ...
  • 十、屬性優先順序 十一、註釋 11.1 <!-- ... --> 同HTML/XML的註釋 11.2 thymeleaf解析器註釋 thymeleaf解析的時候會被註釋,靜態打開時會顯示 單行 <!--/* ... */--> 多行時: <!--/*--> ... ... <!--/*--> 11.3 ...
  • 閱讀本文需要有React的基礎知識,可以在React 入門實例教程和React中文官網進行基礎學習。 沒有React基礎也可以學習本文,本文主要不是學習React,而是gulp+browserify進行模塊化開發。 1.創建項目和環境搭建 我們可以先創建一個文件夾叫react_item,作為項目的根 ...
  • 數據綁定這塊兒沒啥說的,簡單兩個例子帶過了。 Hello World: <!DOCTYPE html> <html ng-app> <head> <title>Simple app</title> <script src="https://ajax.googleapis.com/ajax/libs/ ...
  • 直接貼代碼先看 HTML: CSS: Javascript: 效果圖: PS: jQ和圖片路徑自己載入! ...
  • 這個時候,首先調用Alien構造函數運行,創建一個空對象alien,把構造函數中的this綁定到alien上,然後運行Actor.call(alien,scene,0,0)產生了alien.id=++Actor.nextID,然後添加其它私有屬性,但當又遇到id這個屬性的時候,alien.id=++... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...