Salesforce Javascript(一) Promise 淺談

zero.zhang發表於2020-09-21

本篇參看:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises

 我們在做lwc開發的時候,必須要檢視lwc的開發文件。上圖我們可以經常用到,lwc的wire adapter提供的建立記錄的方法,我們可以看到return的內容是一個Promise物件,而且文件中涉及到非同步建立或者載入文件等方法通常說都返回一個Promise物件,對於前端不好的小夥伴可能想的是, Promise是啥怎麼用呢?

一. Promise是什麼

說起Promise定義和應用以前,我們先從他的漢語以及日常業務場景進行簡單的解釋。

Promise 的中文可以解釋為承諾,做一個簡單的業務場景分析。
年會了,老闆喝酒的時候和你碰了個杯,小張啊,今年表現不錯,10天以後就是績效評定,今年必須給你升職加薪。

對於此時的你來說,兩種想法。

A. 老闆這麼器重我啊,馬上就要升職加薪、迎娶白富美、走上人生巔峰,想想還有點小激動。我要更加努力奮鬥balabala。

B. 可能老闆喝多了,算了,我還是安安靜靜的搬好我的磚吧。

所以說承諾可能具有以下的情況。

1. 承諾將給你一個保證,這個保證自己做別人做怎麼實行,不清楚也不重要,重要的是他給了你承諾,你可以在這個承諾的基礎上做你計劃的事情。上面的例子中,老闆給了你承諾,不管是HR聯絡你還是老闆找你談,不重要,重要的是老闆給你了一個約定,給你升職加薪。
2. 既然是一個承諾,他就會有兩種情況,要麼遵守,要麼違約。
3. 雖然我們不知道這個承諾當時的狀態,但是我們可以計劃一下我們期望從這個承諾達到什麼東西,也需要去計劃一下如果違背了承諾我們怎麼處理。比如漲薪以後買個手機或者出去旅遊等等,如果不漲薪就安心搬磚或者跳槽等等。
4. 承諾應該具有時效性。老闆10天以內沒有聯絡你,半年以後老闆想起來了,哎呀,我之前忘了,今年一定要給你升職加薪。這對於你來說已經不重要了,因為已經過了時效,預設就應該認為是違約了。
5. 承諾通常都不是當時就一下子做的,通常都應該有一定時間進行非同步操作。

 所以我們通過中文的瞭解的現實承諾的場景,去了解一下 js中的Promise的概念。

先看一下MDN上面對於Promise的解釋:Promise 物件用於表示一個非同步操作的最終完成 (或失敗), 及其結果值.也就是說 Promise通常用於非同步操作或者載入資源或者IO等等非同步有阻塞的操作。我們對 Promise有了一個大概的使用場景的瞭解,那麼 Promise如何宣告如何使用呢,接下來慢慢展開。先看一下 Promise的宣告

new Promise( function(resolve, reject) {...} /* executor */  );

引數是一個叫做 executor的函式,包括了兩個引數, resolve 以及reject,當然這兩個引數也都是函式。當 Promise建立以後,會立即執行  executor函式,此時的狀態為pending,executor通常操作非同步函式,當非同步的結果回來有兩種可能,成功或者失敗。成功的情況下會回撥 resolve並且將promise的狀態更改為fulfilled,失敗的情況下會將Promise的狀態更改為rejected並且呼叫 reject函式,如果非同步丟擲了異常,則Promise的狀態變成 rejected,executor的返回值將被忽略。

這樣描述亂亂的,將這個分拆一些細節點。

1.  Promise只擁有三種狀態: pending (初始狀態)、 fulfilled(操作成功)、rejected(操作失敗)。其中fulfilled & rejected也可以概括成settled(已定型)。

2.  Promise 物件是一個代理物件(代理一個值),被代理的值在Promise物件建立時可能是未知的。它允許你為非同步操作的成功和失敗分別繫結相應的處理方法(handlers)。 這讓非同步方法可以像同步方法那樣返回值,但並不是立即返回最終執行結果,而是一個能代表未來出現的結果的promise物件。

Promise最終只允許兩個結果: pending -> fulfilled 或者是 pending -> rejected。Promise方法封裝了一個 then方法,包括兩個引數, onfulfilled以及onRejected,這兩個引數都是函式型別,當 pending -> fulfilled ,會呼叫 onfulfilled函式,當pending -> rejected,則會呼叫 onRejected函式。

通過這張圖可以看出來, Promise不管是執行了 onFulfillment還是 onRejection,返回的型別仍然是 Promise,這就意味著,可以進行多個 Promise的套用。我們在lwc中通常也會遇見多個呼叫的方式,比如 通過 getRecord獲取 account 以後,理論上返回一個 Promise,我們在 then操作時,可以繼續請求後臺,去獲取 關聯的Opportunity等等,Promise可以實現俄羅斯套娃方式的呼叫。

解釋的很難以理解,來個例子簡單瞭解一下。方法中我們宣告瞭一個Promise,在函式中,呼叫了 resolve,為了模擬非同步操作,使用定時器模擬一下,然後呼叫 then函式去列印輸出。

let promise = new Promise(function(resolve,reject) {
    setTimeout(() => resolve(2),1000);
});
promise.then((res) => console.log(res));

結果如下:初始化的時候, Promise是 Pending狀態,當1秒以後,呼叫了 resolve方法,所以輸出了2, 此時 promise的狀態應該是 fulfilled。

 再來看一下reject的效果

let promise = new Promise(function(resolve,reject) {
    setTimeout(() => reject(2),1000);
});
promise.then((res) => console.log(res));

結果如下:

 上圖中我們還截圖了Promise的方法,Promise原型的方法返回的仍然是 Promise,有三個方法可供選擇:then/ catch/ finally。舉個例子更好的瞭解。我們針對 Promise也通常這麼書寫。

let bossPromise = new Promise(function(resolve, reject) {
  resolve({
      title: "TL",
      increase: "1000"
    });
});
bossPromise.then(function(result) {
  console.log("哇,我漲薪了,好開心啊 ,老闆說我漲了" + JSON.stringify(result));
});
bossPromise.catch(function(reason) {
  console.log("沒有漲薪,因為老闆說我:" + reason);
});
bossPromise.finally(function() {
  console.log("漲與不漲都能接受,我都愛公司和這份工作");
});

結果展示:通過截圖可以看到,Promise呼叫了 resolve,狀態變成了fulfilled,呼叫了 then函式,需要注意的是,不管呼叫了 then還是catch,都需要呼叫 finally函式。

reject相關的操作:

let bossPromise = new Promise(function(resolve, reject) {
  reject({
      reason : '能力還得慢慢提高'
    });
});
bossPromise.then(function(result) {
  console.log("哇,我漲薪了,好開心啊 ,老闆說我漲了" + JSON.stringify(result));
});
bossPromise.catch(function(reason) {
  console.log("沒有漲薪,因為老闆說我:" + JSON.stringify(reason));
});
bossPromise.finally(function() {
  console.log("漲與不漲都能接受,我都愛公司和這份工作");
});

結果展示:

看到了 then catch操作是不是想象到了 當我們呼叫後臺方法的語法,會有 then 和catch,想的沒錯,這個操作也是基於 Promise實現。

 二. Promise在lwc中的使用

前面說過,Promise主要用於非同步或者載入資源,因為非同步lwc已經封裝好了,所以說我們在lwc中使用 Promise大部分是載入資源用。比如我們需要使用外部的css / javascript。上傳到 static resource以後,如何引入使之在lwc中有效呢?下面的步驟展示如何載入css或者js資源的步驟。

1. 引入 loadScript或者loadStyle用於javascript / css資源;

import { loadScript, loadStyle } from 'lightning/platformResourceLoader';

2.從static resource引入相關的資源

import xxx from '@salesforce/resourceUrl/xxx';

3. 使用 Promise.all用來執行一組 Promise, loadScript / loadStyle是lwc封裝好的方法,用來執行 Promise並且返回型別也是 Promise。 Promise.all方法詳情可以檢視上面的連線。我們只需要 renderedCallback呼叫 Promise.all即可。詳情參看:https://developer.salesforce.com/docs/component-library/documentation/en/lwc/js_third_party_library總方法如下:

// libsD3.js
/* global d3 */
import { LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import D3 from '@salesforce/resourceUrl/d3';
import DATA from './data';

export default class LibsD3 extends LightningElement {
    svgWidth = 400;
    svgHeight = 400;

    d3Initialized = false;

    renderedCallback() {
        if (this.d3Initialized) {
            return;
        }
        this.d3Initialized = true;

        Promise.all([
            loadScript(this, D3 + '/d3.v5.min.js'),
            loadStyle(this, D3 + '/style.css')
        ])
            .then(() => {
                this.initializeD3();
            })
            .catch(error => {
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error loading D3',
                        message: error.message,
                        variant: 'error'
                    })
                );
            });
    }

    initializeD3() {
        console.log('test');
    }
}

總結:篇中只是簡單描述Promise的介紹以及使用,有錯誤地方歡迎指出,有不懂歡迎留言。

相關文章