[英]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(異步)執行之后呢?
返回值與解決承諾:
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);
});
...當承諾被分配給p1
, setTimeout
已經被調用並安排了五秒后的回調。 無論您是否偵聽 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")
});
發生的事情是:
new Promise
被調用setTimeout
來調度回調"first resolved"
解決了承諾new Promise
返回並且已解決的new Promise
被分配給p1
"first"
輸出到控制台然后你做:
p1.then((val) => {
console.log("(1)", val)
return p2
})
// ...
從那then
總是異步調用它的回調,這是異步發生的——但很快,因為承諾已經解決了。
因此,當您運行該代碼時,您會看到所有三個 Promise在第一個setTimeout
回調發生之前都已解決——因為這些 Promise 不會等待setTimeout
回調發生。
您可能想知道為什么在控制台中看到"third"
之前看到最后的then
回調運行,因為承諾解析和console.log("third")
都是異步發生的,但很快(因為它是setTimeout(..., 0)
並且承諾都是預先解決的):答案是承諾解決是微任務,而setTimeout
調用是宏任務(或只是“任務”)。 任務調度的所有微任務在該任務完成后立即運行(並且它們調度的任何微任務也將被執行),然后從任務隊列中取出下一個任務。 所以運行你的腳本的任務是這樣的:
setTimeout
回調安排任務p1
的then
回調then
處理程序,調度一個微任務來運行第二個then
處理程序then
處理程序運行並調度一個微任務來調用第三個then
處理程序then
處理程序都運行完畢p3
的setTimeout
回調,所以它會運行並且"third"
出現在控制台中
- 返回值與解決承諾:
您在問題中提出的部分對我來說沒有意義,但您對此的評論確實如此:
我讀到返回一個值或解決一個承諾是一樣的......
什么你可能讀過的是,從返回一個值then
或catch
是一樣的,從返回一個解決的承諾then
或catch
。 那是因為then
和catch
在被調用時創建並返回新的承諾,如果他們的回調返回一個簡單的(非承諾)值,他們會使用該值解析他們創建的承諾; 如果回調返回一個承諾,他們會根據該承諾是解決還是拒絕來解決或拒絕他們創建的承諾。
所以例如:
.then(() => {
return 42;
})
和
.then(() => {
return new Promise(resolve => resolve(42));
})
具有相同的最終結果(但第二個效率較低)。
在then
或catch
回調中:
then
/ catch
throw ...
)拒絕使用你拋出的值的承諾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.