簡體   English   中英

Promises - 如何在沒有 async / await 的情況下使異步代碼同步執行?

[英]Promises - How to make asynchronous code execute synchronous without async / await?

  var p1 = new Promise(function(resolve, reject) {  
    setTimeout(() => resolve("first"), 5000);
  });
  var p2 = new Promise(function(resolve, reject) {  
    setTimeout(() => resolve("second"), 2000);
  });
  var p3 = new Promise(function(resolve, reject) {  
    setTimeout(() => resolve("third"), 1000);
  });

  console.log("last to print");

p1.then(()=>p2).then(()=>p3).then(()=> console.log("last to be printed"))

當我閱讀有關承諾時,我知道當我使用 async /await 時,我可以同步打印承諾(在這種情況下是打印:第一個、第二個、第三個、最后一個打印)。 現在我也讀到同樣的事情可以使用 .then 鏈接來實現,而 async/await 並不是什么“特別”的東西。 但是,當我嘗試鏈接我的承諾時,除了“最后打印”的 console.log 之外,什么也沒有發生。 任何見解都會很棒! 謝謝!!

編輯質疑:

  var p1 = new Promise(function (resolve, reject) {
    setTimeout(() => console.log("first"), 5000);
    resolve("first resolved")
  });
  var p2 = new Promise(function (resolve, reject) {
    setTimeout(() => console.log("second"), 2000);
    resolve("second resolved")
  });
  var p3 = new Promise(function (resolve, reject) {
    setTimeout(() => console.log("third"), 0);
    resolve("third resolved")
  });

  console.log("starting");
  p1.then((val) => {
    console.log("(1)", val)
    return p2
  }).then((val) => {
    console.log("(2)", val)
    return p3
  }).then((val) => {
    console.log("(3)", val)
  })

日志:

starting
(1) first resolved
(2) second resolved
(3) third resolved
third
second
first

1:如果傳遞給 new Promise 的 executor 函數是立即執行的,在新的 Promise 返回之前,那么為什么這里的 promise 先()同步)在 setTimeouts(異步)執行之后呢?

  1. 返回值與解決承諾:

    var sync = function () { return new Promise(function(resolve, reject){ setTimeout(()=> { console.log("start") resolve("hello") //--works // return "hello" //--> 什么都不做 }, 3000); }) } sync().then((val)=> console.log("val", val))

您傳遞給new Promise的 executor 函數會在返回new Promise之前立即執行。 所以當你這樣做時:

var p1 = new Promise(function(resolve, reject) {  
  setTimeout(() => resolve("first"), 5000);
});

...當承諾被分配給p1setTimeout已經被調用並安排了五秒后的回調。 無論您是否偵聽 promise 的解析,都會發生該回調,無論您是通過await關鍵字還是then方法偵聽解析都會發生該回調。

所以你的代碼立即啟動三個setTimeouts ,然后開始等待第一個承諾的解決,然后才等待第二個承諾的解決(它已經解決了,所以這幾乎是立即的),然后等待第三個(再次相同)。

要讓您的代碼僅在前一個超時完成時按順序執行這些setTimeout調用,您必須在前一個承諾解決之前不要創建新的承諾(使用較短的超時來避免大量等待):

 console.log("starting"); new Promise(function(resolve, reject) { setTimeout(() => resolve("first"), 1000); }) .then(result => { console.log("(1) got " + result); return new Promise(function(resolve, reject) { setTimeout(() => resolve("second"), 500); }); }) .then(result => { console.log("(2) got " + result); return new Promise(function(resolve, reject) { setTimeout(() => resolve("third"), 100); }); }) .then(result => { console.log("(3) got " + result); console.log("last to print"); });

請記住,promise 不會做任何事情,也不會改變promise executor 中代碼的性質。 所有的承諾確實是提供觀察的東西(有非常方便的組合語義)結果的一種手段。

讓我們將這三個 promise 的共同部分分解為一個函數:

function delay(ms, ...args) {
    return new Promise(resolve => {
        setTimeout(resolve, ms, ...args);
    });
}

然后代碼變得清晰一點:

 function delay(ms, ...args) { return new Promise(resolve => { setTimeout(resolve, ms, ...args); }); } console.log("starting"); delay(1000, "first") .then(result => { console.log("(1) got " + result); return delay(500, "second"); }) .then(result => { console.log("(2) got " + result); return delay(100, "third"); }) .then(result => { console.log("(3) got " + result); console.log("last to print"); });

現在,讓我們把它放在一個async函數中並使用await

 function delay(ms, ...args) { return new Promise(resolve => { setTimeout(resolve, ms, ...args); }); } (async() => { console.log("starting"); console.log("(1) got " + await delay(1000, "first")); console.log("(2) got " + await delay(500, "second")); console.log("(3) got " + await delay(100, "third")); console.log("last to print"); })();

通過標准化我們觀察異步過程的方式,Promise 使這種語法成為可能。


重新編輯:

1:如果傳遞給 new Promise 的 executor 函數是立即執行的,在新的 Promise 返回之前,那么為什么這里的 promise 先()同步)在setTimeouts (異步)執行之后呢?

這個問題有兩個部分:

A) “......為什么這里承諾首先解決()同步)......”

B) “......為什么這里承諾解決......在setTimeouts (異步)被執行之后”

(A) 的答案是:雖然你同步解析它們, then總是異步調用它的回調。 這是承諾提供的保證之一。 您正在執行程序函數返回之前解析p1 (在該編輯中)。 但是您觀察分辨率的方式可確保您按順序觀察分辨率,因為在p1解決之前您不會開始觀察p2 ,然后在p2解決之前您不會開始觀察p3

(B) 的答案是:它們沒有,您正在同步解析它們,然后異步觀察這些解析,並且由於它們已經被解析,因此發生的速度非常快; 稍后,計時器回調運行。 讓我們看看如何在該編輯中創建p1

var p1 = new Promise(function (resolve, reject) {
  setTimeout(() => console.log("first"), 5000);
  resolve("first resolved")
});

發生的事情是:

  1. new Promise被調用
  2. 它調用執行器函數
  3. executor 函數調用setTimeout來調度回調
  4. 您立即通過"first resolved"解決了承諾
  5. new Promise返回並且已解決的new Promise被分配給p1
  6. 稍后,超時發生,您將"first"輸出到控制台

然后你做:

p1.then((val) => {
  console.log("(1)", val)
  return p2
})
// ...

從那then總是異步調用它的回調,這是異步發生的——但很快,因為承諾已經解決了。

因此,當您運行該代碼時,您會看到所有三個 Promise第一個setTimeout回調發生之前都已解決——因為這些 Promise 不會等待setTimeout回調發生。

您可能想知道為什么在控制台中看到"third"之前看到最后的then回調運行,因為承諾解析和console.log("third")都是異步發生的,但很快(因為它是setTimeout(..., 0)並且承諾都是預先解決的):答案是承諾解決是任務,setTimeout調用是宏任務(或只是“任務”)。 任務調度的所有微任務在該任務完成后立即運行(並且它們調度的任何微任務也將被執行),然后從任務隊列中取出下一個任務。 所以運行你的腳本的任務是這樣的:

  1. setTimeout回調安排任務
  2. 安排一個微任務來調用p1then回調
  3. 當任務結束時,處理它的微任務:
    1. 運行第一個then處理程序,調度一個微任務來運行第二個then處理程序
    2. 第二個then處理程序運行並調度一個微任務來調用第三個then處理程序
    3. 等等,直到所有then處理程序都運行完畢
  4. 從任務隊列中選取下一個任務。 它可能是p3setTimeout回調,所以它會運行並且"third"出現在控制台中
  1. 返回值與解決承諾:

您在問題中提出的部分對我來說沒有意義,但您對此的評論確實如此:

我讀到返回一個值或解決一個承諾是一樣的......

什么你可能讀過的是,返回一個值thencatch是一樣的,從返回一個解決的承諾thencatch 那是因為thencatch在被調用時創建並返回新的承諾,如果他們的回調返回一個簡單的(非承諾)值,他們會使用該值解析他們創建的承諾; 如果回調返回一個承諾,他們會根據該承諾是解決還是拒絕來解決或拒絕他們創建的承諾。

所以例如:

.then(() => {
    return 42;
})

.then(() => {
    return new Promise(resolve => resolve(42));
})

具有相同的最終結果(但第二個效率較低)。

thencatch回調中:

  1. 返回一個 non-promise 解決了使用該值創建的 promise then / catch
  2. 拋出錯誤( throw ... )拒絕使用你拋出的值的承諾
  3. 返回承諾使then / catch的承諾根據回調返回的承諾解決或拒絕

您不能使異步代碼同步執行。

即使async / await只是語法,給你一個承諾同步式的控制流。

但是,當我嘗試鏈接我的承諾時,除了“最后打印”的 console.log 之外,什么也沒有發生。 任何見解都會很棒!

其他函數不生成任何輸出。 這與他們在承諾中無關。

您啟動三個計時器(全部同時),然后記錄“最后打印”,然后鏈接一些承諾,以便在所有三個承諾都解決時(在您開始所有承諾后 5 秒)打印“最后打印”。

如果您希望計時器按順序運行,那么您必須僅在前一個計時器完成時才啟動它們,如果您想查看它們的解析結果,則必須編寫實際查看它的代碼。

 function p1() { return new Promise(function(resolve, reject) { setTimeout(() => resolve("first"), 5000); }); } function p2() { return new Promise(function(resolve, reject) { setTimeout(() => resolve("second"), 2000); }); } function p3() { return new Promise(function(resolve, reject) { setTimeout(() => resolve("third"), 1000); }); } function log(value) { console.log("Previous promise resolved with " + value); } p1() .then(log) .then(p2) .then(log) .then(p3) .then(log) .then(() => console.log("last to be printed"));

Async/await 可以說更簡潔:

 function p1() { return new Promise(function(resolve, reject) { setTimeout(() => resolve("first"), 5000); }); } function p2() { return new Promise(function(resolve, reject) { setTimeout(() => resolve("second"), 2000); }); } function p3() { return new Promise(function(resolve, reject) { setTimeout(() => resolve("third"), 1000); }); } function log(value) { console.log("Previous promise resolved with " + value); } (async function() { log(await p1()); log(await p2()); log(await p3()); console.log("last to be printed"); }());

如果您需要調用 await 但包含該 await 的函數不必是異步的,例如,因為您需要一個“數字”而不是“承諾數字”,您可以執行以下操作:

var ex: number = new Number(async resolve => {
              var f = await funcionExample();                  
              resolve(f);
            }).valueOf();

暫無
暫無

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

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