JS: Promise

来源:https://www.cnblogs.com/guolao/archive/2019/02/25/10433577.html
-Advertisement-
Play Games

Promise 是 JavaScript 非同步編程中的重要概念,是目前較為流行的 JavaScript 非同步編程解決方案之一。它最早由社區提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了 對象。 Promise 的基本概念 一個 有以下幾種狀態: pending: 初始狀態,既不是成功 ...


Promise

Promise是 JavaScript 非同步編程中的重要概念,是目前較為流行的 JavaScript 非同步編程解決方案之一。它最早由社區提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise對象。

Promise 的基本概念


一個 Promise有以下幾種狀態:

  • pending: 初始狀態,既不是成功,也不是失敗狀態(也可以說是進行中)。
  • fulfilled: 意味著操作成功完成。
  • rejected: 意味著操作失敗。

pending 狀態的 Promise 對象可能觸發 fulfilled 狀態並傳遞一個值給相應的狀態處理方法,也可能觸發失敗狀態(rejected)並傳遞失敗信息。當其中任一種情況出現時,Promise 對象的 then 方法綁定的處理方法(handlers )就會被調用(then方法包含兩個參數:onfulfilled 和 onrejected,它們都是 Function 類型。當Promise狀態為 fulfilled 時,調用 then 的 onfulfilled 方法,當 Promise 狀態為 rejected 時,調用 then 的 onrejected 方法, 所以在非同步操作的完成和綁定處理方法之間不存在競爭)。 ------MDN

因為Promise.prototype.thenPromise.prototype.catch 方法會返回 promise 對象,所以它們可以被鏈式調用

Promise

Promise 的基本用法


// 這是 Promise 的常用方法
function promise(...) {
    return new Promise((resolve, reject) => {
        // do something
        if (/* fulfilled (非同步操作成功) */) {
            return resolve(value);
        } 
    reject(error);
    });
}

promise(...).then((value) => {
    // success
}, (error) => {
    // failure
});

Promise構造函數接受一個函數作為參數,該函數的兩個參數分別是resolvereject。它們是兩個函數,由 JavaScript 引擎提供。

當狀態從 pending變為fulfilled時,即非同步操作成功,這時調用resolve,並將非同步操作的結果,作為參數傳遞出去。當狀態從 pending變為rejected時,即非同步操作失敗,這時調用reject,並將error作為參數傳遞出去。

Promise實例生成後,通過then方法部署fulfilled狀態和rejected狀態的回調函數。

下麵是一個用Promise封裝 Ajax 的例子:

const getJSON = function({
    // 解構賦值設置預設參數
    url = 'url', 
    method = 'GET', 
    async = false, 
    data = null
    } = {}) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open(url, method, async);
        xhr.responseType = 'json';
        xhr.setRequestHeader('Accept', 'application/json');
        xhr.onreadystatechange = function() {
            if (this.readyState == 4) {
                let status = this.status;
                if (status >= 200 && status < 300 || status == 304) {
                    resolve(this.response);
                } else {
                    reject(this.statusText);
                }
            }
        }
        xhr.send(data);
    });
}

getJSON({url: '/json'})
    .then(json => console.log(json), error => console.log(error));

Promise 的鏈式調用


以上面封裝的 Ajax 為例子:

getJSON({url: '/json'})
.then( json => getJSON({url: json.oneUrl}) )
.then( json => getJSON({url: json.twoUrl}) )
.catch( error => {...} )
.then( () => {...} );

因為then或者catch方法會返回一個Promise對象,或者自己return一個Promise對象,而這樣就可以繼續使用then或者catch方法。

Promise.prototype.catch()


這裡的catch()try/cath的方法類似,都是捕捉錯誤進行處理。

// 這兩種寫法是等價的
// 第一種
getJSON({url: '/json'}).then( (json) => {
    // success
}).catch( (error) => {
    // failure
});

// 第二種
getJSON({url: '/json'}).then((json) => {
    // success
},(error) => {
    // failure
});

但比較建議使用第一種寫法,因為使用catch讓代碼更接近於同步代碼,而且在鏈式調用中,使用catch方法可以捕捉前面所有的錯誤進行一併處理。

Promise對象的錯誤具有“冒泡”性質,會一直向後傳遞,直到被捕獲為止,而且catch還會返回一個Promise對象,這樣可以繼續使用then方法進行鏈式調用。

getJSON({url: '/json'})
.then( json => getJSON({url: json.oneUrl}) )
.then( json => getJSON({url: json.twoUrl}) )
.then( json => {...} )
.catch( error => {
    // 這裡可以處理前面所有 Promise 的錯誤
}).then( () => {...} );

Promise.prototype.finally()


finally裡面的操作不管 Promise 對象最後狀態如何,都是會去執行的,這和 JAVA 的 finally 有點相似,即無論如何,一定執行。

promise(...)
.then(result => {···})
.catch(error => {···})
.finally(() => {
    // 這裡的操作一定執行
});

值得註意的是,finally方法的回調函數是不接受任何參數的,這意味著沒有辦法知道前面的 Promise 狀態到底是fulfilled還是rejected。所以finally方法裡面的操作,應該是與狀態無關的,不依賴於 Promise 的執行結果。

Promise.resolve()


Promise.resolve(value)方法返回一個以給定值解析後的promise對象。

但如果這個值是個 thenable(即帶有then方法),返回的promise會“跟隨”這個thenable的對象,採用它的最終狀態(指resolved/rejected/pending/settled);

如果傳入的value本身就是promise對象,則該對象作為Promise.resolve方法的返回值返回;

否則以該值為成功狀態返回promise對象。

  • 傳入參數是一個promise對象

    如果傳入參數本身就是 Promise 實例,那麼Promise.resolve不做任何修改、原封不動地返回這個實例。

    const p0 = new Promise((resolve, reject) => {
      resolve(1);
    });
    const p1 = Promise.resolve(p0);
    console.log(p1 === p0); // true
  • 傳入參數是thenable對象

    Promise.resolve()會將這個對象轉為 Promise 對象,然後立即執行thenable對象的then方法,執行完畢之後,Promise 對象狀態變為fulfilled狀態,然後就會執行後續的then方法。

    // thenable 即帶有 then 方法的對象
    let thenable = {
        then: (resolve, reject) => {
            resolve(1);
        }
    };
    const p2 = Promise.resolve(thenable);
    p2.then( value => console.log(value) );
  • 傳入參數為其他值

    如果參數不為前面的兩種的情況,則Promise.resolve方法會以fulfilled狀態返回一個新的 Promise 對象,並將傳入的參數傳給then方法。

    const p3 = Promise.resolve(123);
    p3.then( value => console.log(value) ); // 123

Promise.reject()


Promise.reject(value)會以rejected狀態返回一個 Promose 實例,並將傳入參數原封不動地傳給then或者catch方法。

const p4 = Promise.reject('錯誤');
// then()
p4.then(null, (err)=>{
        console.log(err); // 錯誤
    });
// catch()
p4.catch( err => console.log(err) ); // 錯誤

Promise.all()


Promise.all(iterable) 方法返回一個 Promise實例,此實例在 iterable 參數內所有的 promise 狀態都為fulfilled狀態(即成功)或參數中不包含 promise 時回調完成;如果參數中 promise 有一個狀態為rejected狀態(即失敗)時回調失敗,失敗的原因是第一個失敗 promise 的結果。

一般來說,Promise.all方法會傳入一個數組參數,如果數組成員里有非Promise實例時,會自動調用上面所說的Promise.resolve方法將其轉為Promise實例,再進行處理。

然後處理結果分兩種情況:

1.參數內所有Promise實例的狀態都為fulfilled狀態,則返回一個包含所有resolve結果的數組傳給後續的回調函數。

2.參數內有一個Promise實例的狀態為rejected狀態,則將第一個被rejectPromise實例的結果傳給後續的回調函數。

const p5 = Promise.resolve(123);
const p6 = 42;
const p7 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo');
});

Promise.all([p5, p6, p7]).then((values) => {
    console.log(values); // [123, 42, "foo"]
});

Promise.race()


Promise.race方法與上面的Promise.all()類似,但不同的是,Promise.race()是只要傳入的Promise實例中有一個狀態改變了,就會直接返回這個最快改變狀態的Promise實例的結果,而不是返回所有。

const p8 = Promise.resolve(123);
const p9 = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo');
});

Promise.race([p8, p9]).then((values) => {
    // 這裡只會獲得 p8 的返回值
    console.log(values); // 123
});

手動實現 Promise.all()


有的面試會讓實現Promise.all()方法,這個還算簡單的,有的還要手撕Promise

首先來理清一下實現思路:

  • 遍歷執行傳入的Promise實例。

  • 如果傳入的參數成員有不是Promise實例的,會直接調用Promise.resolve()進行處理。
  • 返回一個新的Promise實例,分兩種情況返回結果。
  • fulfilled狀態返回的數組結果要按順序排列。

實現代碼如下:

function promiseAll(promises) {
  // 返回一個新的 Promise 實例
  return new Promise((resolve, reject) => {
    let count = 0;
    let promisesNum = promises.length;
    let resolvedValue = new Array(promisesNum);
    for (let i=0; i<promisesNum; i++) {
        // 用 Promise.resolve() 處理傳入參數
        Promise.resolve(promises[i])
           .then((value) => {
              // 順序排列返回結果
              resolvedValue[i] = value;
              if (++count == promisesNum) {
                  return resolve(resolvedValue);
              }
            })
            .catch((err) => {
              // 返回第一個 rejected 狀態的結果
              return reject(err);
        });
    }
  });
}

測試如下:

// 成功的情況
const p1=Promise.resolve(1);
const p2=Promise.resolve(2);
const p3=Promise.resolve(3);

promiseAll([p1, p2, p3]).then(console.log); // [1, 2, 3]
// 失敗的情況
const p1=Promise.resolve(1);
const p2=Promise.reject('失敗');
const p3=Promise.resolve(3);

promiseAll([p1, p2, p3])
  .then(console.log)
  .catch(console.log); // 失敗

備註


春招開始了,有點不敢投,躊躇著,也焦慮著。


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

-Advertisement-
Play Games
更多相關文章
  • 原文鏈接:https://www.cnblogs.com/wtujvk/p/7497723.html 運行程式時拋出異常: 基礎提供程式在 Open 上失敗,詳細信息:該帳戶當前被鎖定,所以用戶sa登錄失敗。系統管理員無法將該帳戶解鎖。 1.考慮連接字元串是否正常 登錄資料庫,發現登錄報同樣的錯誤 ...
  • 一、 PCH文件的作用 Xcode中,PCH文件在程式編譯的時候會自動包含進去。也就是說PCH中的內容是全局的,可以使用在程式的任何地方,通過這個特性,我們可以概括到PCH的作用有以下幾個方面: (1)將經常使用的巨集定義在該文件,可以避免多次定義的麻煩 (2)包含多次使用的.h文件 (3)其他需要全 ...
  • 1. RunLoop 簡介 1.1 什麼是 RunLoop? 可以理解為字面意思:Run 表示運行,Loop 表示迴圈。結合在一起就是運行的迴圈的意思。哈哈,我更願意翻譯為『跑圈』。直觀理解就像是不停的跑圈。 RunLoop 實際上是一個對象,這個對象在迴圈中用來處理程式運行過程中出現的各種事件(比 ...
  • 這次我們依舊來談談有關性能優化的話題,這次我們會用到Google給我們提供的分析工具——Systrace。如果你還不瞭解這個工具,最好先瞭解一下。Google 官方文檔: https://developer.android.com/studio/command line/systrace 我們還會用 ...
  • 什麼是閉包? 簡單理解,當在一個函數的外部訪問函數內部定義的變數的時候就會形成一個閉包,由這個理解可以知道,當一個函數執行完成的時候,一般情況下,其作用域會被銷毀,其內部定義的變數也會變得不可訪問,所以閉包打破了這個現象。閉包造成一個函數執行完成之後,其創建的作用域不會被銷毀,因為它被函數外部的對象 ...
  • 年三十時 vue2.6 發佈,向 3.0 看齊,說明 3.0 不遠了。作為開發者也應該為vue3.0 做點準備。首先是把 vue-cli 升級到 3.x ,在這記錄下 vue-cli2.x 升級 vue-cli3.x 中遇見(將來)遇見的問題。 1、安裝 vue-cli3.x 如果希望還保留 vue ...
  • // canvas畫圖 // 顏色漸變 var grd = draw.createLinearGradient(0,0,175,50); grd.addColorStop(0, '#f00'); grd.addColorStop(0.5, '#0f0'); grd.addColorStop(1.0, ...
  • 個人很喜歡谷歌的material design,很喜歡但是沒有動手弄過,今天想動手操作一下Floating Action Button菜單,網上有很多種:圓形、扇形、射線、直線等。我想在一個例子中用到這幾種展現形式,觸發按鈕在不同的位置,菜單用不同的方式展示…… 基於這種需求,開始著手準備,此時遇到 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...