摘要:首先構造函數(shù)中需要有一些狀態(tài)和方法因為執(zhí)行實例邏輯的時候需要這些維護好的狀態(tài)和值其中著重提醒的就是的狀態(tài)機是單向的且狀態(tài)單向不可逆。
引言
16年時在公司分享過一次promise,猶記得當時是第一次分享,還蠻緊張的,當時分享的主要是promise的使用和基本原理,后來又給無線部門同學分享了一次。
現(xiàn)在回顧想想,其實講的不是很完美,因為我當時的實現(xiàn)方式類似于簡化版q庫的實現(xiàn),考慮的也不全面,也沒有完全遵循promise/a+規(guī)范。經(jīng)過這么長一段時間的學習和積累,闡述一下自己新的理解。
在沒有promise以前,多個有依賴的異步操作一般寫出來會出現(xiàn)嵌套,所謂的回調地域,這種寫法在需要協(xié)作的項目中不方便維護,異步操作也不能直接捕獲異常,需要回調中進行處理,缺點挺多的,然后就開始漫長的優(yōu)化,出現(xiàn)了q, bluebird,jq中的defer等這些庫,后來ES6標準實現(xiàn)了Promise,但是其鏈式寫法還是不美觀,為了代碼更優(yōu)雅,可以視覺上同步命令式的書寫代碼有了TJ大神的co再結合generator似乎完美了,但是為了優(yōu)雅還要額外引入co庫,成本有點大,后來ES7標準干脆直接實現(xiàn)了,就是所謂的async和await語法糖
Promise定義現(xiàn)在開始切入正題,什么是Promise呢? 簡而言之promise代表承諾,專業(yè)術語就是代表一個異步操作的最終結果。
代碼層面來看的話Promise是一個類,可以用來創(chuàng)建實例,每個實例內部封裝一些方法,且維護了一些狀態(tài)和值,通過使用這些狀態(tài)、值和方法來將現(xiàn)實流程中的承諾具體代碼化表示。
promise主要提供了then,catch,all,race,resolve,reject這幾個方法,關于這幾個方法怎么使用不在贅述,因為占據(jù)文章篇幅長,且很多其它blog重復描述過。推薦阮一峰es6入門中相關api用法解釋,詳細且全面。
關于具體應用的話,由于在工作中項目基于vue技術棧,所以結合axios時會使用到promise來操作異步,還有就是m站基于pwa,其中Service worker聲明周期事件處理中會涉及promise,還有一些就是平時寫node工具的時候會用到,用promise封裝異步api操作回調,從而將異步api回調邏輯直接放到then方法中進行處理。
基于Promise/a+規(guī)范實現(xiàn)的代碼能互相統(tǒng)一,雖然代碼形式會有不同,但原理都差不多。
首先Promise構造函數(shù)中需要有一些狀態(tài)和方法,因為執(zhí)行實例then邏輯的時候需要這些維護好的狀態(tài)和值,其中著重提醒的就是promise的狀態(tài)機是單向的,且狀態(tài)單向不可逆。
狀態(tài)轉變只能是 pending -> fulfilled 或者 pending -> rejected。
//構造函數(shù)初始化邏輯 let that = this; //緩存this //默認狀態(tài)為pending that.status = "pending"; //此變量里放著此promise的結果 that.value = undefined; //存放的著所有成功的回調函數(shù) that.onResolvedCallbacks = []; //存放著所有的失敗的回調函數(shù) that.onRejectedCallbacks = [];
其中內部resolve和reject邏輯如下,更改狀態(tài)機狀態(tài),觸發(fā)承諾邏輯執(zhí)行
function resolve(value) { //更改狀態(tài) 執(zhí)行then注冊的成功回調邏輯 if (that.status == "pending") { //解決resolve 新Promise這種情況 if(value!=null &&value.then&&typeof value.then == "function"){ return value.then(resolve,reject); } that.status = "fulfilled"; that.value = value; that.onResolvedCallbacks.forEach(item=>item(that.value)); } } function reject(reason) { //更改狀態(tài) 執(zhí)行then注冊的失敗回調邏輯或者catch中注冊的失敗邏輯 if (that.status == "pending") { that.status = "rejected"; that.value = reason; that.onRejectedCallbacks.forEach(item=>item(that.value)); } }
上面已經(jīng)介紹了大致初始化邏輯了,下面著重介紹使用頻率最高的then方法,簡潔版實現(xiàn)如下所示
PPromise.prototype.then = function (onFulfilled, onReject) { //成功和失敗的邏輯沒有傳遞 會進行值的穿透 傳遞給下一個then方法 onFulfilled = isFunction(onFulfilled) ?onFulfilled:val =>val; onReject = isFunction(onReject) ?onReject:reason => {throw reason;} let self = this,promise2; switch (self.status){ case "fulfilled": promise2 = new Promise((resolve,reject) =>{ let x = onFulfilled(self.value); if(x instanceof Promise){ //遞歸執(zhí)行then邏輯 直到內部then執(zhí)行,外部promise2被resolve x.then(resolve,reject) }else{ resolve(x); } }); break case "rejected": promise2 = new Promise((resolve,reject) =>{ let x = onReject(self.value); if(x instanceof Promise){ x.then(resolve,reject) }else{ resolve(x); } }) break case "pending": promise2 = new Promise((resolve,reject) =>{ self.onResolvedCallbacks.push(function(){ let x = onFulfilled(self.value); if(x instanceof Promise){ x.then(resolve,reject) }else{ resolve(x); } }); self.onRejectedCallbacks.push(function(){ let x = onReject(self.value); if(x instanceof Promise){ x.then(resolve,reject) }else{ resolve(x); } }); }); } return promise2; }
all方法實現(xiàn)
function sentry(times,cb){ let result = [],count=0; return function(i,data){ result[i] = data; if(++count==times){ cb(result); } } } Promise.all = function(promises){ return new Promise((resolve,reject) => { //利用閉包機制,目的是為了判斷promises是否都執(zhí)行完 let done = sentry(promises.length,resolve); for(let i=0;i{ done(i,data); },reject); } }); }
resolve實現(xiàn)
Promise.resolve = function(value){ return new Promise(function(resolve){ resolve(value); }); }
race實現(xiàn)
Promise.race = function(promises){ return new Promise((resolve,reject) =>{ for(let i=0;i自己實現(xiàn)的promise源碼
異步優(yōu)雅寫法異步操作經(jīng)過promisify轉化成promise,在結合async實現(xiàn)優(yōu)雅的寫法
let Promise = require("bluebird"); let readFile = Promise.promisify(require("fs").readFile); async function read() { let a = await readFile("./1.txt","utf8"); let b = await readFile("./2.txt","utf8"); let c = await readFile("./3.txt","utf8"); console.log(c); return "ok"; } read().then(data => { console.log(data); });總結任何事物都不是一蹴而就的,都有一個發(fā)展過程才逐步變得完美,將自己的學習坐下記錄,并加一些個人思考,如果對于本文有任何疑問或錯誤,歡迎斧正交流。
參考鏈接
https://promisesaplus.com/
https://segmentfault.com/a/11...
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/92450.html
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務器端的列表,第三方服務器提供了一個接口,可通過...
摘要:的翻譯文檔由的維護很多人說,阮老師已經(jīng)有一本關于的書了入門,覺得看看這本書就足夠了。前端的異步解決方案之和異步編程模式在前端開發(fā)過程中,顯得越來越重要。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。 JavaScript Promise 迷你書(中文版) 超詳細介紹promise的gitbook,看完再不會promise...... 本書的目的是以目前還在制定中的ECMASc...
摘要:簽訂協(xié)議的兩方分別是異步接口和。在異步函數(shù)中,使用異常捕獲的方案,代替了的異常捕獲的方案。需要注意的是,在異步函數(shù)中使異步函數(shù)用時要使用,不然異步函會被同步執(zhí)行。 同步與異步 通常,代碼是由上往下依次執(zhí)行的。如果有多個任務,就必需排隊,前一個任務完成,后一個任務才會執(zhí)行。這種執(zhí)行模式稱之為: 同步(synchronous) 。新手容易把計算機用語中的同步,和日常用語中的同步弄混淆。如,...
摘要:到這里,我已經(jīng)發(fā)出了一個請求買漢堡,啟動了一次交易。但是做漢堡需要時間,我不能馬上得到這個漢堡,收銀員給我一個收據(jù)來代替漢堡。到這里,收據(jù)就是一個承諾保證我最后能得到漢堡。 同期異步系列文章推薦談一談javascript異步javascript異步中的回調javascript異步之Promise.all()、Promise.race()、Promise.finally()javascr...
摘要:則是把類似的異步處理對象和處理規(guī)則進行規(guī)范化,并按照采用統(tǒng)一的接口來編寫,而采取規(guī)定方法之外的寫法都會出錯。這個對象有一個方法,指定回調函數(shù),用于在異步操作執(zhí)行完后執(zhí)行回調函數(shù)處理。到目前為止,已經(jīng)學習了創(chuàng)建對象和用,方法來注冊回調函數(shù)。 Promise 本文從js的異步處理出發(fā),引入Promise的概念,并且介紹Promise對象以及其API方法。 js里的異步處理 可以參考這篇文章...
摘要:宏任務和微任務這兩個是指兩個隊列,腳本整體代碼的回調及渲染都會被加入到隊列中回調瀏覽器實現(xiàn)回調都會被加入到隊列。 1. macrotask (宏任務)和 microtask (微任務) 這兩個是指兩個隊列,腳本整體代碼、setTimeout、setInterval、setImmediate、I/O、的回調及UI渲染都會被加入到 macrotask 隊列中, process.nextTi...
閱讀 2800·2021-11-22 14:44
閱讀 541·2021-11-22 12:00
閱讀 3683·2019-08-30 15:54
閱讀 1570·2019-08-29 17:15
閱讀 1898·2019-08-29 13:50
閱讀 1107·2019-08-29 13:17
閱讀 3513·2019-08-29 13:05
閱讀 1181·2019-08-29 11:31