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
  • 移動開發(一):使用.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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...