簡體   English   中英

JavaScript承諾如何執行其代碼?

[英]How do JavaScript promises execute their code?

這是我從https://scotch.io/tutorials/javascript-promises-for-dummies竊取的代碼,並做了一些修改。

var momIsHappy = false;

var getAPhone = function (whatToDoIfPhoneIsPurchased,
                          whatToDoIfPhoneIsNotPurchased) {
  fulfillPromiseToPurchasePhone = whatToDoIfPhoneIsPurchased;
  breakPromiseToPurchasePhone = whatToDoIfPhoneIsNotPurchased;

  if (momIsHappy) {
      var phone = {
          brand: 'Samsung',
          color: 'black'
      };
      fulfillPromiseToPurchasePhone(phone); // fulfilled
  } else {
      var reason = new Error('mom is not happy');
      breakPromiseToPurchasePhone(reason); // reject
  }
}

var aPhoneIMightGet = new Promise(getAPhone);

var playWithNewPhone = function () {
    findOutIfIGetPhone = aPhoneIMightGet;

    findOutIfIGetPhone
    .then(function (aPhone) {
        // yay, you got a new phone
        console.log(aPhone);
     // output: { brand: 'Samsung', color: 'black' }
    })
    .catch(function (error) {
        // oops, mom don't buy it
        console.log(error.message);
     // output: 'mom is not happy'
    });
};

我不明白的是:什么時候調用getAPhone 我沒打電話 誰叫的 什么時候? 怎么樣?

讓我們備份一下並描述Promise構造函數的作用。 Promise構造函數傳遞給一個同步調用的回調。 該回調(通常稱為“ Promise executor回調”)背后的想法是,它將啟動一些異步操作,然后立即返回。 稍后,當異步操作完成后,它將解析或拒絕該承諾。

這是一個簡單的示例,我們可以逐步進行操作(您可以在下面的代碼段中運行此示例以查看日志記錄順序):

 console.log("0"); let p = new Promise(function(resolve, reject) { console.log("1"); setTimeout(function() { console.log("2"); resolve("done"); }, 1000); }).then(function(val) { console.log("3"); }); console.log("4"); 

您將獲得以下輸出:

0     // start of the code
1     // Promise executor callback called and setTimeout() initiated
4     // Promise created and now initialized
2     // setTimeout fires
3     // Promise gets resolved and .then() handler called

而且,這是發生的事件的順序:

  1. console.log("0"); 運行
  2. 您調用new Promise()並將其傳遞給回調函數
  3. Promise構造函數運行,創建一個Promise對象並同步調用回調函數
  4. 回調函數的目的是啟動一些非阻塞的異步操作(在這種情況下,是一個簡單的setTimeout() ),但是它可能要復雜得多,例如執行一些文件操作,搜索數據庫等。 。
  5. 回調函數被傳遞兩個參數,每個參數都是一個函數。 當回調中啟動的異步操作完成時,回調應調用這兩個函數之一。 如果這是典型的異步操作,則回調將啟動異步操作,然后返回。 然后一段時間后,將調用與異步操作關聯的不同回調,然后根據該操作是成功還是返回錯誤,該回調將調用傳遞給該回調的兩個函數之一(我將其命名為resolvereject在我的示例中,您的示例選擇了更長的名稱。
  6. 在我的代碼示例中,Promise回調(稱為Promise執行程序)立即登錄到控制台,然后調用setTimeout() 由於setTimeout()是非阻塞且異步的,因此它將啟動計時器並立即返回。 此時,Promise執行程序回調已完成並返回。
  7. 計時器現在正在運行,promise執行程序回調已運行並返回。 promise構造函數完成后,它返回新的Promise對象。
  8. 然后,在promise上調用.then()方法。 這會為promise注冊一個完成回調,然后立即返回。 從技術上講,它返回第二個Promise,該Promise鏈接到使用Promise構造函數創建的第一個Promise。 已注冊的此.then()回調已保存,但尚未調用。
  9. 現在,此示例中的最后一行代碼將執行,並將4記錄到控制台。
  10. 此代碼已完成(目前)。 隨后執行的任何其他代碼將立即執行,或者如果沒有其他代碼,則Javascript解釋器將返回事件循環並等待下一個事件發生。
  11. 稍后,計時器啟動。 它在Javascript事件隊列中插入一個事件,當解釋器完成其他操作后,再從事件隊列中獲取下一個事件,它將調用傳遞給setTimeout()的回調。
  12. 執行該setTimeout()回調時,它將記錄2 ,然后調用resolve("done") 調用resolve()解析第一個promise,並告訴它隨后觸發任何.then()處理.then()並調用它們。
  13. 此時,將調用傳遞給.then()的回調並將其記錄為3 ,一切.then()完成。

現在回答有關代碼示例的一些特定問題:

我不明白的是:什么時候調用getAPhone? 我沒打電話 誰叫的 什么時候? 怎么樣?

您將getAPhone傳遞給Promise構造函數。 Promise構造函數將其作為運行構造函數以創建新Promise對象的一部分進行調用。

這是一個簡單的例子,說明如何發生:

function doIt(someCallback) {
    someCallback("hi");
}

function myCallback(greeting) {
   console.log(greeting);
}

doIt(myCallback);    // logs "greeting"

調用doIt時,會向其傳遞函數引用(本質上是指向函數的指針)。 這樣,您要調用的函數就可以在調用函數時使用其想要的任何參數來調用該函數。 在這種情況下, doIt()希望您向其傳遞一個函數引用,它將立即調用該函數並將其傳遞給"hi"

那么,為什么promise構造函數在運行我的函數時不只是阻塞我的代碼? 如果構造函數所做的所有事情都立即為我調用了一個函數,為什么它與直接調用該函數有什么不同?

promise構造函數是同步運行的(當您調用new Promise() )。 Promise構造函數同步運行Promise執行程序回調函數。 但是,它通常所做的只是啟動異步和非阻塞操作,然后立即返回。 然后,異步操作將在后台運行,並在將來的某個時候觸發它自己的回調。

那么,為什么Promise的構造函數中沒有代碼塊呢? 它不是像其他所有函數一樣在調用堆棧上具有函數嗎?

Promise構造函數正在阻塞。 它直到完成才返回。 與其他所有函數一樣,它是調用堆棧上的函數。

在這里可能使您感到困惑的是,您的getAPhone()示例不包含任何異步代碼。 這是完全同步的。 完全沒有理由在該代碼中使用promise。

這是該函數的簡化版本:

var momIsHappy = false;

var getAPhone = function (resolve, reject) {
  if (momIsHappy) {
      var phone = {
          brand: 'Samsung',
          color: 'black'
      };
      resolve(phone); // fulfilled
  } else {
      var reason = new Error('mom is not happy');
      reject(reason); // reject
  }
}

所有這些操作就是檢查momIsHappy ,然后立即解決或拒絕該承諾。 這里沒有異步操作。 完全沒有理由在您的示例中使用promise。 承諾是用於管理和協調異步操作的工具。 如果您沒有任何異步操作,則不應使用它們,因為當可以使用普通和更簡單的函數調用時,它們會增加同步代碼的復雜性。 使用異步操作,可能難以協調和管理異步操作以及錯誤處理,尤其是當有多個異步操作需要排序或協調並且處理所有錯誤時,尤其如此。 這就是承諾的目的。

因此,在您的特定情況下,您可以將getAPhone()變成常規函數,直接調用它,而根本不使用promises。

用一個簡單的例子來測試就不難了:

 function someFun() { console.log("testing") } let p = new Promise(someFun) 

因此,即使我們對諾言不做任何事情,當您做出諾言並同步運行時,諾言構造函數仍會調用該函數。 在更現實的情況下,您可以向它傳遞一個函數,該函數帶有asolve回調,該回調在異步操作完成時執行,但是傳遞給new Promise()的函數仍會立即觸發:

 function someFun(resolve) { console.log("someFun called") setTimeout(() => resolve("promise resolved"), 1500) } console.log("starting") let p = new Promise(someFun) .then(console.log) console.log("just called the promise") 

我不明白的是:何時調用“ getAPhone”? 我沒打電話 誰叫的 什么時候? 怎么樣?

也許可以更直接地回答這個問題:當您將新的Promise分配給變量“ aPhoneIMightGet”時,構造函數將按照上面的Mark Meyer的說明運行。

您的“ getAPhone”是傳遞給Promise構造函數的“執行程序函數”。 這樣,它會在Promise構造函數返回之前立即執行。 這就是所謂的“ getAPhone”功能。

可以在這里找到可能會有所幫助的好文章。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM