摘要:在處理異步回調(diào)函數(shù)的情況有著越來(lái)越值得推崇的方法及類庫(kù),下面會(huì)依次介紹處理異步函數(shù)的發(fā)展史,及源碼解讀。而對(duì)象的狀態(tài),是由第一個(gè)的參數(shù)成功回調(diào)函數(shù)或失敗回調(diào)函數(shù)的返回值決定的。
函數(shù)的執(zhí)行分為同步和異步兩種。
同步即為 同步連續(xù)執(zhí)行,通俗點(diǎn)講就是做完一件事,再去做另一件事。
異步即為 先做一件事,中間可以去做其他事情,稍后再回來(lái)做第一件事情。
同時(shí)還要記住兩個(gè)特性:1.異步函數(shù)是沒(méi)有返回值的,return不管用哦 2.try{}catch(e){}不能捕獲異步函數(shù)中的異常。
js在處理異步回調(diào)函數(shù)的情況有著越來(lái)越值得推崇的方法及類庫(kù),下面會(huì)依次介紹js處理異步函數(shù)的發(fā)展史,及源碼解讀。
(本文代碼是運(yùn)行在node環(huán)境中)
let fs = require("fs"); fs.readFile("./1.txt","utf8",function(err,data){ console.log(data); })
如果只有一個(gè)異步請(qǐng)求,那用callback還好,但是相信大多數(shù)前端開(kāi)發(fā)者都遇到過(guò)這兩種情況:
a.一個(gè)異步請(qǐng)求獲取到的結(jié)果是下一個(gè)異步請(qǐng)求的參數(shù)。(一直嵌套callback,代碼不好管理會(huì)形成回調(diào)地獄);
let fs = require("fs"); fs.readFile("./1.txt","utf8",(err,data)=>{ fs.readFile(data,"utf8",(err,data)=>{ console.log(data); }) })
b.發(fā)出兩個(gè)請(qǐng)求,只有當(dāng)兩個(gè)請(qǐng)求都成功獲取到數(shù)據(jù),在執(zhí)行下一步操作。
let fs =require("fs"); fs.readFile("./1.txt","utf8",(err,data)=>{ console.log(data); }) fs.readFile("./2.txt","utf8",(err,data)=>{ console.log(data); })
像類似這種情況,只有當(dāng)讀取到1.txt 和2.txt的文件的時(shí)候,我們同時(shí)獲取到兩個(gè)異步請(qǐng)求的結(jié)果。我們可以寫一個(gè)計(jì)數(shù)器的函數(shù),統(tǒng)一處理回調(diào);
function after(time,callback){ let arr = []; return function(data){ arr.push(data) if(--time==0){ callback(arr); } } } //統(tǒng)一處理回調(diào)結(jié)果的回調(diào)傳到after函數(shù)中。 let out = after(2,(res)=>{console.log(res)}); let fs =require("fs"); fs.readFile("./1.txt","utf8",(err,data)=>{ out(data); }) fs.readFile("./2.txt","utf8",(err,data)=>{ out(data); })
tips:
方便我們更好的了解計(jì)數(shù)器的實(shí)現(xiàn)原理,我們需要了解一個(gè)概念:高階函數(shù)
高階函數(shù):可以把函數(shù)作為參數(shù) 或者 return返回出一個(gè)函數(shù)。
舉個(gè)例子:
①.判斷一個(gè)變量是不是屬于一個(gè)類型:
function isType(type,content){ return Object.protoType.toString.call(content) ==`[Object ${type}]` } let a = [1,2,3]; isType("Array", a) == true;
②.js數(shù)據(jù)類型有好多,我們每次調(diào)用都要傳入他的類型,麻不麻煩。所以我們寫一個(gè)方法,可以批量生成函數(shù)。
function isType(type){ return function(content){ return Object.protoType.toString.call(content) == `[Oject ${type}]` } } let isArray = isType("Array"); let a = [1,2,3] isArray(a);
前兩種示例講的是return返回一個(gè)函數(shù),下面示例是一個(gè)預(yù)置函數(shù)及返回函數(shù)參數(shù)的結(jié)合示例(預(yù)置函數(shù))。
③.場(chǎng)景加入我有一個(gè)函數(shù),執(zhí)行第三次的時(shí)候我想輸出"我很可愛(ài)";平常我們可以這樣去實(shí)現(xiàn):
let time =0; function say(){ if(++item==3){ console.log("我很可愛(ài)") } } say(); say(); say();
高階函數(shù)實(shí)現(xiàn)的話:
function after(time,callback){ return function(){ if(--time ==0){ callback(); } } } function say(){ console.log("我很可愛(ài)"); } let out =after(3,say) out(); out(); out();
高階函數(shù)實(shí)現(xiàn)了將計(jì)時(shí)任務(wù)與業(yè)務(wù)邏輯拆分,高階函數(shù)的實(shí)現(xiàn)主要得益于作用域的查找。
2.Promise在看完了上面的callback講述,主要其實(shí)還是講述了callback的弊端:
a.回調(diào)地獄(callback無(wú)法解決)
b.并發(fā)請(qǐng)求,同時(shí)拿到結(jié)果(可通過(guò)計(jì)數(shù)器方式,但是太費(fèi)勁,不太樂(lè)觀)
這個(gè)時(shí)候duang~duang~duang~,ES6帶著Promise來(lái)了~
Promise主要是es6提供的主要用于處理異步請(qǐng)求的一個(gè)對(duì)象,他能夠很好的解決回調(diào)地獄以及并發(fā)請(qǐng)求。
在寫promise源碼之前,我們先通過(guò)幾個(gè)調(diào)用promise的示例,了解一下promise的一些原理及特性,這在我們封裝promise的時(shí)候能夠起到很大的作用:
普通調(diào)用實(shí)例:
let fs = require("fs"); let p = new Promise(function(resolve,reject){ fs.readFile("./1.txt","utf8",(err,data)=>{ err?reject(err):resolve(data); }) }) p.then((data)=>{console.log(data)},(err)=>{console.log(err)});
1.promise實(shí)例可以多次調(diào)用then方法;
p.then((data)=>{console.log(data)},(err)=>{console.log(err)}); p.then((data)=>{console.log(data)},(err)=>{console.log(err)});
2.promise實(shí)例可以支持then方法的鏈?zhǔn)秸{(diào)用,jquery實(shí)現(xiàn)鏈?zhǔn)绞峭ㄟ^(guò)返回當(dāng)前的this。但是promise不可以通過(guò)返回this來(lái)實(shí)現(xiàn)。因?yàn)楹罄m(xù)通過(guò)鏈?zhǔn)皆黾拥膖hen不是通過(guò)原始的promise對(duì)象的狀態(tài)來(lái)決定走成功還是走失敗的。
p.then((data)=>{console.log(data)},(err)=>{console.log(err)}).then((data)=>{console.log(data)})
3.只要then方法中的成功回調(diào)和失敗回調(diào),有返回值(包括undefiend),都會(huì)走到下個(gè)then方法中的成功回調(diào)中,并且把返回值作為下個(gè)then成功回調(diào)的參數(shù)傳進(jìn)去。
第一個(gè)then走成功: p.then((data)=>{return undefined},(err)={console.log()}).then((data)=>{console.log(data)}) 輸出:undefiend 第一個(gè)then走失?。? p.then((data)=>{console.log(1)},(err)={return undefined).then((data)=>{console.log(data)}) 輸出:undefiend
4.只要then方法中的成功回調(diào)和失敗回調(diào),有一個(gè)拋出異常,則都會(huì)走到下一個(gè)then中的失敗回調(diào)中;
第一個(gè)then走成功: p.then((data)=>{throw new Err("錯(cuò)誤")},(err)={console.log(1)}).then((data)=>{console.log("成功")},(err)=>{console.log(err)}) 輸出:錯(cuò)誤 第一個(gè)then走失?。? p.then((data)=>{console.log(1)},(err)={throw new Err("錯(cuò)誤")).then((data)=>{console.log("成功")},(err)=>{console.log(err)}) 輸出:錯(cuò)誤
5.成功和失敗 只能走一個(gè),如果成功了,就不會(huì)走失敗,如果失敗了,就不會(huì)走成功;
6.如果then方法中,返回的不是一個(gè)普通值,仍舊是一個(gè)promise對(duì)象,該如何處理?
答案:它會(huì)等待這個(gè)promise的執(zhí)行結(jié)果,并且傳給下一個(gè)then方法。如果成功,就把這個(gè)promise的結(jié)果傳給下一個(gè)then的成功回調(diào)并且執(zhí)行,如果失敗就把錯(cuò)誤傳給下一個(gè)then的失敗回調(diào)并且執(zhí)行。
7.具備catch捕獲錯(cuò)誤;如果catche前面的所有then方法都沒(méi)有失敗回調(diào),則catche會(huì)捕獲到錯(cuò)誤信息執(zhí)行他就是用來(lái)兜兒底用的。
p是一個(gè)失敗的回調(diào): p.then((data)=>{console.log("成功")}).then((data)=>{成功}).catche(e){console.log("錯(cuò)誤")}
8.返回的結(jié)果和 promise是同一個(gè),永遠(yuǎn)不會(huì)成功和失敗
var r = new Promise(function(resolve,reject){ return r; }) r.then(function(){ console.log(1) },function(err){ console.log(err) })
以上是經(jīng)過(guò)調(diào)用es6提供的promise,發(fā)現(xiàn)的一些特性,下面我們會(huì)根據(jù)這些特性去封裝Promise類。
一.我們先通過(guò)初步了解的promise和簡(jiǎn)單的基本調(diào)用,簡(jiǎn)單的實(shí)現(xiàn)一個(gè)promise;
1.Promise支持傳入一個(gè)參數(shù),函數(shù)類型,這個(gè)函數(shù)往往是我們自己發(fā)起異步請(qǐng)求的函數(shù),我們稱它為執(zhí)行器actuator,這個(gè)函數(shù)會(huì)在調(diào)用new Promise()的作用域內(nèi)立即執(zhí)行,并且傳入兩個(gè)函數(shù)一個(gè)resolve另一個(gè)是reject作為參數(shù);
2.promise對(duì)象支持.then()的方法,then方法支持兩個(gè)參數(shù)一個(gè)為onFulfilled成功回調(diào)另一個(gè)為onRejected失敗回調(diào);onFulfilled接受參數(shù)data為異步請(qǐng)求拿到的數(shù)據(jù),onRejected接受的參數(shù)為捕獲到的異常錯(cuò)誤。
3.當(dāng)異步回調(diào)成功時(shí),執(zhí)行resolve,并且把回調(diào)結(jié)果傳給resolve函數(shù)。失敗則執(zhí)行reject,把異常信息傳給reject函數(shù)。(這一步往往是在actuator執(zhí)行器函數(shù)中我們自己去控制執(zhí)行的)
4.一個(gè)promise對(duì)象,執(zhí)行了resolve,就不會(huì)在去執(zhí)行reject。執(zhí)行了reject,也不會(huì)在去執(zhí)行resolve;
所以promise內(nèi)部中有一個(gè)類似狀態(tài)機(jī)的機(jī)制,它分為三種狀態(tài),創(chuàng)建一個(gè)promise對(duì)象,默認(rèn)狀態(tài)為"pending"狀態(tài),當(dāng)執(zhí)行了resolve,則該狀態(tài)變?yōu)?fulfilled",若果執(zhí)行了reject則該狀態(tài)變?yōu)?rejected",所以我們?cè)趖hen方法中需要根據(jù)狀態(tài)作出判斷;
5.promise對(duì)象已經(jīng)是成功狀態(tài)或是失敗狀態(tài)時(shí),都可以繼續(xù)通過(guò)then傳入函數(shù),會(huì)通過(guò)當(dāng)前的狀態(tài),來(lái)決定執(zhí)行成功還失敗,并且把結(jié)果或是錯(cuò)誤傳給相應(yīng)的函數(shù)。所以我們需要拿到的結(jié)果和捕獲的錯(cuò)誤。
function Promise(fn){ this.status = "pending";//狀態(tài)機(jī) //一個(gè)promise支持執(zhí)行多個(gè)then,所以需要一個(gè)池子把他的回調(diào)函數(shù)存儲(chǔ)起來(lái),統(tǒng)一遍歷執(zhí)行; this.onFulfilledCallbacks = []; this.onRejectedCallbacks =[]; //保存結(jié)果或者錯(cuò)誤異常 this.result = "";//當(dāng)前promise回調(diào)成功獲取到的數(shù)據(jù); this.reason = "";//當(dāng)前promise失敗的原因 var self = this; function resolve(data){ //執(zhí)行了reject就不能執(zhí)行resolve,所以必須保證是pending狀態(tài); //當(dāng)執(zhí)行回調(diào)成功,在執(zhí)行器調(diào)用resolve,我們?nèi)ケ闅v成功回調(diào)的池子,依次執(zhí)行; //保存結(jié)果,并且將當(dāng)前狀態(tài)設(shè)置為"fulfilled" if(self.status=="pending"){ self.result = data; self.status = "fulfilled"; self.onFulfilledCallbacks.forEach((fn)=>{ fn(data); }) } } function reject(err){ //執(zhí)行了resolve就不能執(zhí)行reject,所以必須保證是pending狀態(tài); //當(dāng)執(zhí)行回調(diào)失敗,在執(zhí)行器調(diào)用reject,我們?nèi)ケ闅v成功回調(diào)的池子,依次執(zhí)行; //保存錯(cuò)誤原因并且將當(dāng)前狀態(tài)設(shè)置為"rejected" if(self.status=="pending"){ self.reason= err; self.status ="rejected"; self.onRejectedCallbacks.forEach((fn)=>{ fn(err); }) } } fn(resolve,reject) } Promise.prototype.then= function(onFulfilled,onRejected){ //如果當(dāng)前promise對(duì)象成功狀態(tài),則直接執(zhí)行onFulfilled回調(diào)函數(shù),并且把拿到的已經(jīng)保存的成功數(shù)據(jù)傳進(jìn)去。 if(this.status =="fulfilled"){ onFulfilled(this.result) } //如果當(dāng)前promise對(duì)象失敗狀態(tài),則直接執(zhí)行rejected回調(diào)函數(shù),并且把已經(jīng)保存的補(bǔ)貨失敗的原因傳進(jìn)去。 if(this.status =="rejected"){ onRejected(this.reason); } if(this.status == "pending"){ this.onFulfilledCallbacks.push(onFulfilled); this.onRejectedCallbacks.push(onRejected); } }
到目前為止我們已經(jīng)封裝了一個(gè)簡(jiǎn)易版的promise了,我們可以通過(guò)一些case去測(cè)試一下,是否滿足上面所描述的特性。
let fs = require("fs"); let p = new Promise((resolve,reject)=>{ fs.readFile("./1.txt","utf8",function (err,data) { err ? reject(err):resolve(data); }) }); p.then(data=>{console.log(data)},err=>{console.log(err)}); p.then(data=>{console.log(data)},err=>{console.log(err)});
二、我們簡(jiǎn)易版的promise類,已經(jīng)初步實(shí)現(xiàn)了一些promise的基本特性;這一節(jié)我們我們簡(jiǎn)易版的promise進(jìn)行改版,把promise的更復(fù)雜的功能增加進(jìn)去。
1.當(dāng)我們調(diào)用promise時(shí),傳入的執(zhí)行器會(huì)立刻執(zhí)行,執(zhí)行器函數(shù)內(nèi)部是一個(gè)同步的過(guò)程,我們可以用try...catch捕獲錯(cuò)誤,并且應(yīng)該直接調(diào)用失敗的函數(shù)。
2.promise支持鏈?zhǔn)綄懛ǎ瑃hen后面繼續(xù).then ,原理并不是像jquery一樣返回一個(gè)this;而是不管當(dāng)前promise狀態(tài)是什么,都返回一個(gè)新的promise對(duì)象,官方文檔命名這個(gè)新的promise對(duì)象為promise2。
3.鏈?zhǔn)綄懛ㄖ械诙€(gè)then中的回調(diào)走成功還是走失敗,取決于上一個(gè)then中返回的promise(就是promise2)對(duì)象的狀態(tài)。 而 promise2對(duì)象的狀態(tài),是由第一個(gè)then的參數(shù)(成功回調(diào)函數(shù)或失敗回調(diào)函數(shù))的返回值決定的。如果返回的是一個(gè)值(包括返回的是undefined、""),則第二個(gè)then走成功;如果返回的仍舊是一個(gè)promise對(duì)象,那么promise2會(huì)等待返回的這個(gè)promise對(duì)象的回調(diào)結(jié)果而確定promise2的狀態(tài)值,如果回調(diào)結(jié)果拿到的是一個(gè)值(成功),那么promise2會(huì)將此值作為參數(shù)傳入字節(jié)的reosolve中并執(zhí)行,如果回調(diào)中拋出異常(失?。?,那么promise2會(huì)把異常傳到reject中并且執(zhí)行;
function Promise(fn){ this.status = "pending"; this.onFulfilledCallbacks = []; this.onRejectedCallbacks =[]; this.result = ""; this.reason = ""; var self = this; function resolve(data){ if(self.status=="pending"){ self.result = data; self.status = "fulfilled"; self.onFulfilledCallbacks.forEach((fn)=>{ fn(data); }) } } function reject(err){ if(self.status=="pending"){ self.reason= err; self.status ="rejected"; self.onRejectedCallbacks.forEach((fn)=>{ fn(err); }) } } try{ fn(resolve,reject) }catch(e){ reject(e) } } Promise.prototype.then= function(onFulfilled,onRejected){ //then方法什么都不傳,也可以支持連續(xù)調(diào)用 onFulfilled = onFulfilled ?onFulfilled :function(data){ return data}; onRejected =onFulfilled ? onFulfilled :function(err){throw new Error(err)} let self = this; let Promise2;//聲明primise2 if(this.status =="fulfilled"){ Promise2 = new Promise(function(resolve,reject){ //promise2的狀態(tài),決定下一個(gè)then方法中執(zhí)行成功還是失敗。 //promise2的狀態(tài),是由第一個(gè)then的onFulfilled的返回值決定的。 //當(dāng)我們執(zhí)行onFulfilled(我們通過(guò)then方法傳進(jìn)來(lái)的自己的函數(shù))的時(shí)候,是同步操作,需要通過(guò)trycatch捕獲異常,如果發(fā)現(xiàn)異常就直接走下一個(gè)then的reject失敗回調(diào)。 //promise官方文檔規(guī)定,每一個(gè)resolve或是reject回調(diào)的執(zhí)行必須保證是在異步中執(zhí)行,所以我們強(qiáng)制加定時(shí)器,保證onFulfilled是異步執(zhí)行的。 setTimeOut(function(){ try{ let x = onFulfilled(self.result); //獲取到返回值,需要去解析,從而判斷出promise2應(yīng)該走失敗還是成功。 resolvePromise(Promise2,x,resolve,reject) }catch(e){ //執(zhí)行reject,下一個(gè)then就會(huì)走失敗 reject(e); } }) }) } if(this.status =="rejected"){ Promise2 = new Promise(function(resolve,reject){ setTimeout(function(){ try{ let x = onRejected(self.reason); resolvePromise(Promise2,x,resolve,reject) }catch(e){ reject(e) } }) }) } if(this.status == "pending"){ Promise2 = new Promise(function(resolve,reject){ self.onFulfilledCallbacks.push(function(){ setTimeout(function(){ try{ let x = onFulfilled(self.result); resolvePromise(Promise2,x,resolve,reject); }catch (e){ reject(e) } }) }); self.onRejectedCallbacks.push(function(){ setTimeout(function(){ try { let x = onRejected(self.reason); resolvePromise(Promise2,x,resolve,reject) }catch (e){ reject(e); } }) }); }) } return Promise2; } function resolvePromise(promise2,x,resolve,reject){ //此處如果相等會(huì)爆出類型錯(cuò)誤; if(promise2 == x){ reject(new TypeError("循環(huán)引用了")) } //如果x是對(duì)象或函數(shù)(引用類型的值),則需要進(jìn)一步判斷。(這塊兒要想的多一些,因?yàn)閤是開(kāi)發(fā)人員寫的函數(shù)返回的,第一個(gè)then中回調(diào)返回的) //若果x是一個(gè)普通值,則直接執(zhí)行resolve,并且傳給下個(gè)then的成功; //如果返回的是一個(gè)promise對(duì)象,則promise2則會(huì)等待返回的promise對(duì)象執(zhí)行完成,如果執(zhí)行完成后,看這個(gè)promise走的成功還是失敗,如果失敗則拋出異常。如果成功則將獲取的數(shù)據(jù)作為onFulfilled返回的結(jié)果,用于判斷promise2走成功或者失敗,因?yàn)榉祷氐慕Y(jié)果可能還是promise對(duì)象,所以用遞歸去執(zhí)行,知道拿到數(shù)據(jù)或者異常。(遞歸) //判斷是不是promise對(duì)象,通過(guò)有沒(méi)有then方法 //捕獲異常是因?yàn)榕袛嗖粐?yán)謹(jǐn),存在then方法,可能也不是promise對(duì)象,調(diào)用它的then可能會(huì)報(bào)錯(cuò)。 let called =false; if(x!==null &&(typeof x =="object"|| typeof x =="function")){ try{ let then =x.then; if(typeof then =="function"){ //promise對(duì)象 then.call(x,function(y){ if(called)return; called = true; resolvePromise(promise2,y,resolve,reject) },function(err){ if(called)return; called = true; reject(err) }) }else{ //普通對(duì)象 resolve(x) } }catch(e){ if(called)return; called = true; reject(e) } }else{ resolve(x); } } 到此,Promise的大部分特性都已經(jīng)具備了。但是Promise對(duì)象還有一些其他的方法,可供調(diào)用,比如說(shuō)catch方法,還有他的私有屬性all 、race、defferd,如果前面的Promise封裝懂了,那這些方法就so easy了,下面會(huì)根據(jù)這些方法的功能一一進(jìn)行封裝,
1.all方法處理 并發(fā)請(qǐng)求,同時(shí)獲得結(jié)果。一個(gè)失敗,則失敗,都成功,才算成功.這個(gè)時(shí)候我們就想到前面我們寫的計(jì)數(shù)器的用法。
Promise.all([read("./1.txt"),read("./2.txt")]).then(res=>{console.log(res)}) Promise.all = function(promiseArray){ return new Promise(function(resolve,reject){ var result = []; var i=0; function processData(index,res){ result[index] = res; if(++i==promiseArray.length){ resolve(result) } } promiseArray.forEach((item,index)=>{ item.then(res=>{processData(index,res)},reject) }) }) };
2.race方法,Pomise.race,顧名思義“賽拍”,傳入多個(gè)異步promise,只要有一個(gè)成功,則就成功,有一個(gè)失敗則失敗,后面也可跟then方法。
Promise.race = function(promiseArray){ return new Promise(function(resolve,reject){ promiseArray.forEach((item,index)=>{ item.then(resolve,reject); }) }) } Promise.race([read("./1.txt"),read("./5.txt")]).then(res=>{console.log(res)},err=>{console.log(err)})
3.生成一個(gè)成功的promise,把傳入的參數(shù),傳入到then的成功回調(diào)中,該方法返回一個(gè)promise
Promise.resolve=function(value){ return new Promise(function(resolve,reject){ //promise規(guī)范 resolve和reject函數(shù)必須是在異步回調(diào)中執(zhí)行 setTimeout(function(){ resolve(value); }) }) } Promise.resolve("123").then(res=>{console.log(res)})
4.生成一個(gè)失敗的promise,把傳入的參數(shù),傳入到then的失敗回調(diào)中。該方法返回一個(gè)promise
Promise.reject = function(err){ return new Promise(function(resolve,reject){ setTimeout(function(){ reject(err); }) }) } Promise.reject("error").then(res=>{console.log(res)},err=>{console.log(err)})
5.catch托底捕獲錯(cuò)誤,這個(gè)方法是實(shí)例的共有方法,應(yīng)該放到Promise的原型上,每一個(gè) promise實(shí)例都可以調(diào)用.它支持一個(gè)參數(shù),該參數(shù)是之前所有的then中,并沒(méi)有失敗回調(diào),當(dāng)發(fā) 生錯(cuò)誤時(shí),最后統(tǒng)一在catch中進(jìn)行捕獲
Promise.prototype.catch = function(calllback){ return this.then(null,callback) }
6.很多人都用過(guò)jquery的deferrd對(duì)象,他和promise的deffer對(duì)象很類似。promise的deferred對(duì)象只是對(duì)promise進(jìn)行了一次封裝
Promise.defer = Promise.deferred=function(){ var obj = {}; obj.promise = new Promise(function(resolve,reject){ obj.resolve = resolve; obj.reject = reject; }) return obj; } let fs = require("fs"); function read2 (url){ var deferr = Promise.deferred(); fs.readFile("./1.txt","utf8",(err,res)=>{ err?deferr.reject(err):deferr.resolve(res); }) return deferr; } read2("./1.txt").then(data=>{console.log(data)})
至此,一個(gè)完整的Promise.js封裝完成,當(dāng)然最后是需要模塊化導(dǎo)出的,我們采用CommonJS規(guī)范導(dǎo)出一個(gè)模塊 采用
module.exports = Promise;
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93554.html
摘要:接下來(lái)我們看下三類異步編程的實(shí)現(xiàn)。事件監(jiān)聽(tīng)事件發(fā)布訂閱事件監(jiān)聽(tīng)是一種非常常見(jiàn)的異步編程模式,它是一種典型的邏輯分離方式,對(duì)代碼解耦很有用處。 一、 一道面試題 前段時(shí)間面試,考察比較多的是js異步編程方面的相關(guān)知識(shí)點(diǎn),如今,正好輪到自己分享技術(shù),所以想把js異步編程學(xué)習(xí)下,做個(gè)總結(jié)。下面這個(gè)demo 概括了大多數(shù)面試過(guò)程中遇到的問(wèn)題: for(var i = 0; i < 3; i++...
摘要:最受歡迎的引擎是,由和使用,用于,以及使用的。引擎它們是如何工作的全局執(zhí)行上下文和調(diào)用堆棧剛剛了解了引擎如何讀取變量和函數(shù)聲明,它們最終被放入了全局內(nèi)存堆中。事件循環(huán)只有一個(gè)任務(wù)它檢查調(diào)用堆棧是否為空。 為了保證可讀性,本文采用意譯而非直譯。 想閱讀更多優(yōu)質(zhì)文章請(qǐng)猛戳GitHub博客,一年百來(lái)篇優(yōu)質(zhì)文章等著你! 有沒(méi)有想過(guò)瀏覽器如何讀取和運(yùn)行JS代碼? 這看起來(lái)很神奇,我們可以通過(guò)瀏覽...
摘要:事件循環(huán)從回調(diào)隊(duì)列中獲取并將其推送到調(diào)用堆棧。如何工作請(qǐng)注意,不會(huì)自動(dòng)將您的回調(diào)函數(shù)放到事件循環(huán)隊(duì)列中。它設(shè)置了一個(gè)計(jì)時(shí)器,當(dāng)計(jì)時(shí)器到期時(shí),環(huán)境將您的回調(diào)函數(shù)放入事件循環(huán)中,以便將來(lái)的某個(gè)事件會(huì)將其選中并執(zhí)行它。 我們將通過(guò)回顧第一篇文章中單線程編程的缺點(diǎn),然后在討論如何克服它們來(lái)構(gòu)建令人驚嘆的JavaScript UI。在文章結(jié)尾處,我們將分享5個(gè)關(guān)于如何使用async / awai...
摘要:異步編程三座大山原型原型鏈作用域閉包同步異步。異步操作執(zhí)行完畢后,再執(zhí)行該回調(diào)函數(shù),確?;卣{(diào)在異步操作之后執(zhí)行?;卣{(diào)函數(shù)本身是我們約定俗成的一種叫法,我們定義它,但是并不會(huì)自己去執(zhí)行它,它最終被其他人執(zhí)行了。 JS異步編程 JS三座大山:原型原型鏈、作用域閉包、同步異步。之前有寫過(guò)自己對(duì)閉包的理解,今天來(lái)總結(jié)一下JS中的異步。 思考(案例來(lái)自stackoverflow): functi...
摘要:以下展示它是如何工作的函數(shù)使用構(gòu)造函數(shù)創(chuàng)建一個(gè)新的對(duì)象,并立即將其返回給調(diào)用者。在傳遞給構(gòu)造函數(shù)的函數(shù)中,我們確保傳遞給,這是一個(gè)特殊的回調(diào)函數(shù)。 本系列文章為《Node.js Design Patterns Second Edition》的原文翻譯和讀書(shū)筆記,在GitHub連載更新,同步翻譯版鏈接。 歡迎關(guān)注我的專欄,之后的博文將在專欄同步: Encounter的掘金專欄 知乎專欄...
閱讀 1446·2021-11-17 09:33
閱讀 3035·2021-10-13 09:39
閱讀 2711·2021-10-09 10:01
閱讀 2455·2021-09-29 09:35
閱讀 3906·2021-09-26 10:01
閱讀 3527·2019-08-26 18:37
閱讀 3155·2019-08-26 13:46
閱讀 1919·2019-08-26 13:39