摘要:原文地址定義是針對異步編程的一種解決方案,能夠抽象處理異步對象以及對其進行操作。當狀態由時執行已完成的回調函數,由時,調用失敗的回調函數。所以,可以在自行使用,保證執行不會出錯,或者使用其他異步編程方式。異步編程的方案后續學習再補充
原文地址
定義Promise是針對異步編程的一種解決方案,能夠抽象處理異步對象以及對其進行操作。Promise并不是Javascript語言的擴展,只是在ES6中將其寫進了語言標準,原生Javascript提供了Promise對象,統一了用法。
功能可以在一定程度上優化回調地獄問題,當需要控制多個異步操作時,代碼結構更加清晰,便于理解。
提供統一的API,書寫更加規范,便于維護,便于控制異步操作。
原理一個 Promise 對象代表一個目前還不可用,但是在未來的某個時間點可以被解析的值。
可以理解為Promise能夠以同步的方式編寫異步代碼,但是有更優的解決方式。
Promise的狀態
Promise對象存在三種狀態:pending、fulfilled、rejected。其中pending 狀態最終一定會變為fulfilled或者是rejected中的一種,且不會再發生改變。整個過程由Promise機制保證不會受到外界干擾。當狀態由pending->fulfilled時執行已完成的回調函數,由pending->rejected時,調用失敗的回調函數。一旦狀態改變,就不會再被修改而一直保持這個狀態。
基本API
實例方法
Promise#then
Promise#catch
靜態方法
Promise.resolve
Promise.reject
Promise.all
Promise.race
問題&思考 什么是同步,什么是異步?大家常說Javascript是單線程的語言,這固然是對的。這里的單線程是說,在JS引擎中負責解釋和執行JavaScript代碼的線程只有一個,每個特定的時刻只有特定的代碼被執行,并阻塞其他代碼。換句話說,JavaScript是單線程,但是瀏覽器是多線程的。除了負責解析JS代碼之外,還存在其他線程,負責HTTP請求,DOM處理等等。
同步可以理解成在一個函數返回時,我就拿到了需要的結果,無需等待。異步則是函數返回時,我沒有得到應該得到的結果,而且要在未來某一時間才能得到。程序執行的順序和任務執行的順序并不始終保持一致。
前端的哪些操作需要使用異步編程?常見的操作有接口請求,文件讀取,定時器操作等。在瀏覽器端耗時很長的操作,最好都異步執行。(鼠標點擊事件、窗口大小拖拉事件等也可以算是異步的)
Promise概念和使用上有哪些需要注意的地方?promise對象的狀態,只有對象處于pending狀態時是可變的,一旦從Pending轉換為Fulfilled或Rejected之后, 這個promise對象的狀態就不會再發生任何變化。也就是說,Promise與Event等不同,在.then 后執行的函數可以肯定地說只會被調用一次,而且只會執行fulfilled或者rejected中的一個。
一般情況下會使用new Promise創建對象,除此之外,也可以使用其他方法。
靜態方法Promise.resolve(value) 可以認為是new Promise()方法的快捷方式,同理Promise.reject(error)也是。
thenable對象和promise對象并不是一回事,thenable對象可以理解為具有.then方法的對象。
可以通過Promise.resolve將thenable對象轉化為promise對象。
promise的鏈式調用可以通過使用.then處理回調函數比較多的情況,一定程度上解決回調地獄的問題。鏈式調用的原理(后續可以多帶帶介紹下)簡單來說是Promise內部有一個defers隊列存放事件,程序執行時.then將下個事件放入,promise狀態變化結束之后出發響應函數執行,然后將事件從隊列中移除。因為每一次調用.then時都會創建一個全新的promise對象,所以保證了鏈式調用可以順利執行。
demo1
前一個task的返回值作為后一個task的參數。不使用鏈式調用,直接調用.then方法時,由于每次都是新建的promise對象,所以value的值始終為100。
// 1: 對同一個promise對象同時調用 then 方法 var aPromise = new Promise(function (resolve) { resolve(100); }); aPromise.then(function (value) { return value * 2; }); aPromise.then(function (value) { return value * 2; }); aPromise.then(function (value) { console.log("1: " + value); // => 100 }) // vs // 2: 對 then 進行 promise chain 方式進行調用 var bPromise = new Promise(function (resolve) { resolve(100); }); bPromise.then(function (value) { return value * 2; }).then(function (value) { return value * 2; }).then(function (value) { console.log("2: " + value); // => 100 * 2 * 2 });
在鏈式調用過程中,每一個.then調用時候都會創建一個全新的promise對象,.then調用結束時會執行對應狀態的回調函數,
但是如果沒有傳入rejected的回調,或者.catch使用的位置不同,都會導致最后的結果不同。
demo2
task全部正常執行:
function taskA() { console.log("Task A"); } function taskB() { console.log("Task B"); } function onRejected(error) { console.log("Catch Error: A or B", error); } function finalTask() { console.log("Final Task"); } var promise = Promise.resolve(); promise .then(taskA) .then(taskB) .catch(onRejected) .then(finalTask);
輸出結果為:
Task A Task B Final Task
當taskA執行失敗時:
function taskA() { console.log("Task A"); throw new Error("throw Error @ Task A") }
輸出結果為:
Task A Error: throw Error @ Task A Final Task
由于taskB中并沒有注冊rejected函數,所以taskB并未執行,而是直接通過catch捕獲到了失敗。
如果在taskB的then中傳入了失敗的回調:
promise .then(taskA) .then(taskB,function(error) { console.log("b-catch-error") }) .catch(onRejected) .then(finalTask);
執行結果為:
Task A b-catch-error Final Task
所以,在多個.then鏈式調用時,不同位置報錯會導致task執行順序不同,執行不同,也許不符合預期。
所以,可以在自行使用try-catch,保證.then執行不會出錯,或者使用其他異步編程方式。
demo3
下面代碼中每一次返回的promise對象都不是同一個對象。
var aPromise = new Promise(function (resolve) { resolve(100); }); var thenPromise = aPromise.then(function (value) { console.log(value); }); var catchPromise = thenPromise.catch(function (error) { console.error(error); }); console.log(aPromise !== thenPromise); // => true console.log(thenPromise !== catchPromise);// => true
理解了這個再來看下面的問題。
demo4 錯誤的調用
function badAsyncCall() { var promise = Promise.resolve(); promise.then(function() { // 任意處理 return 1; }); return promise; }
demo5 正確的調用
function anAsyncCall() { var promise = Promise.resolve(); return promise.then(function() { // 任意處理 return 1; }); }
返回結果
demo4中return 的promise并不是執行過.then的promise,所以并沒有返回預期的結果。在實際開發中要注意這個問題。
但看這個字面上可能會有些疑惑,實際上還是概念上的問題。
使用promise.then(Fulfilled, Rejected)的話,在Fulfilled 中發生異常的話,在Rejected中是捕獲不到這個異常的。原因是promise狀態只能從pending變成fulfilled或者是rejected,且變化完成之后不會再被修改。所以一旦執行了fulfilled,說明promise成功了,此時就已經不會再執行rejected了。
demo6
function onReady(fn) { var readyState = document.readyState; if (readyState === "interactive" || readyState === "complete") { fn(); } else { window.addEventListener("DOMContentLoaded", fn); } } onReady(function () { console.log("DOM fully loaded and parsed"); }); console.log("==Starting==");
如果這段代碼在源文件中出現的位置不同,在控制臺上打印的log消息順序也會不同。 結果不可控這是極力要避免的。
正確的寫法應該是:
function onReadyPromise() { return new Promise(function (resolve, reject) { var readyState = document.readyState; if (readyState === "interactive" || readyState === "complete") { resolve(); } else { window.addEventListener("DOMContentLoaded", resolve); } }); } onReadyPromise().then(function () { console.log("DOM fully loaded and parsed"); }); console.log("==Starting==");
為了避免上述中同時使用同步、異步調用可能引起的混亂問題,Promise在規范上規定 Promise只能使用異步調用方式 。
(后續學習再補充)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/96697.html
摘要:前言使用中,鏈式的調用對于控制異步執行很重要。的鏈式調用是支持鏈式調用的,但是它是不同于上面的鏈式。是調用方法返回自身,但是是調用方法后返回一個新的。的運行機制請參考的運行機制值穿透由于通過沒有成功添加回調函數,發生了值穿透。 前言 使用Promise中,鏈式的調用對于控制異步執行很重要。 鏈式調用 在jQuery的使用中,我們常常使用下面的代碼 $(#app).show().css(...
摘要:所謂的能對狀態進行操作的特權方法,指的就是能對對象的狀態進行等調用的方法,而通常的的話只能在通過構造函數傳遞的方法之內對對象的狀態進行操作。一般會在構造函數中編寫邏輯,什么時候執行回調,什么時候執行回調。 原文地址 1. 在then中使用reject 如果一個promise最初只定義了resolve,但是還想要使用reject怎么辦? 可以在then中返回一個新的promise。這個...
摘要:源碼學習本篇為上一篇源碼學習的補充,主要是來介紹和方法。那個率先改變的實例的返回值,就傳遞給的回調函數。基本介紹可見阮一峰老師的書籍。的狀態由決定,分成兩種情況。只有的狀態都變成,的狀態才會變成,此時的返回值組成一個數組,傳遞給的回調函數。 Promise源碼學習(2) 本篇為上一篇源碼學習(1)的補充,主要是來介紹Promise.all()和Promise.race()方法。閑話少敘...
摘要:回調函數成功回調處理器失敗回調處理器用戶發送一個向百度服務器獲取數據的異步請求無阻塞高并發的的誕生更加嚴重的依賴異步操作才能完成無阻賽高并發的特性。 Promise Promise 是什么? 詞語本意: 發音:[?pr?m?s] 詞性:名詞, 翻譯:許諾,允諾。 MDN解釋 Promise 對象用于一個異步操作。 一個Promise表示一個現在,將來或永不可能可用的值。 按照書寫方...
摘要:等待的基本語法該關鍵字的的意思就是讓編譯器等待并返回結果。這里并不會占用資源,因為引擎可以同時執行其他任務其他腳本或處理事件。接下來,我們寫一個火箭發射場景的小例子不是真的發射火箭 本文由云+社區發表 本篇文章,小編將和大家一起學習異步編程的未來——async/await,它會打破你對上篇文章Promise的認知,竟然異步代碼還能這么寫! 但是別太得意,你需要深入理解Promise后,...
摘要:此時,由于只有一個的狀態能夠確定,所以執行的是唯一那個確定狀態的函數,而不會執行其他的,但是并不會阻止其他的執行。在實際應用中,常用來設置超時操作,比如接口請求超時等。思考這個其實并不是矛盾,接受的是返回的的狀態,與原來的沒有關系。 原文地址 Promise.race // `delay`毫秒后執行resolve function timerPromisefy(delay) { ...
閱讀 2259·2021-09-26 09:55
閱讀 3589·2021-09-23 11:22
閱讀 2156·2019-08-30 15:54
閱讀 1903·2019-08-28 18:03
閱讀 2598·2019-08-26 12:22
閱讀 3432·2019-08-26 12:20
閱讀 1731·2019-08-26 11:56
閱讀 2252·2019-08-23 15:30