JS異步處理系列一 ES6 Promise

參考
深入理解 Promise 五部曲 -- 1.異步問題
深入理解 Promise 五部曲 -- 2.控制權(quán)轉(zhuǎn)換問題
深入理解 Promise 五部曲 -- 3.可靠性問題
深入理解 Promise 五部曲 -- 4.擴展問題
深入理解 Promise 五部曲 -- 5.LEGO
【javascript】異步編年史,從“純回調(diào)”到Promise
阮一峰 ES6入門 Promise
廖雪峰 JavaScript教程 Promise
理清 Promise 的狀態(tài)及使用

一、異步回調(diào)的問題
1.調(diào)用函數(shù)過早

調(diào)用函數(shù)過早的最值得讓人注意的問題, 是你不小心定義了一個函數(shù),使得作為函數(shù)參數(shù)的回調(diào)可能延時調(diào)用,也可能立即調(diào)用。 也即你使用了一個可能同步調(diào)用, 也可能異步調(diào)用的回調(diào)。 這樣一種難以預(yù)測的回調(diào)。

在英語世界里, 這種可能同步也可能異步調(diào)用的回調(diào)以及包裹它的函數(shù), 被稱作是 “Zalgo” (一種都市傳說中的魔鬼), 而編寫這種函數(shù)的行為, 被稱作是"release Zalgo" (將Zalgo釋放了出來)

var a =1
zalgoFunction () {
  // 這里還有很多其他代碼,使得a = 2可能被異步調(diào)用也可能被同步調(diào)用
    [  a = 2  ]
  }
console.log(a)

結(jié)果會輸出什么呢? 如果zalgoFunction是同步的, 那么a 顯然等于2, 但如果 zalgoFunction是異步的,那么 a顯然等于1。于是, 我們陷入了無法判斷調(diào)用影響的窘境。

這只是一個極為簡單的場景, 如果場景變得相當(dāng)復(fù)雜, 結(jié)果又會如何呢?你可能想說: 我自己寫的函數(shù)我怎么會不知道呢?很多時候這個不確定的函數(shù)來源于它人之手,甚至來源于完全無法核實的第三方代碼。我們把這種不確定的情況稍微變得夸張一些: 這個函數(shù)中傳入的回調(diào), 有99%的幾率被異步調(diào)用, 有1%的幾率被同步調(diào)用。

2.調(diào)用次數(shù)過多

這里取《你不知道的javascript(中卷)》的例子給大家看一看:

作為一個公司的員工,你需要開發(fā)一個網(wǎng)上商城, payWithYourMoney是你在確認購買后執(zhí)行的扣費的函數(shù), 由于公司需要對購買的數(shù)據(jù)做追蹤分析, 這里需要用到一個做數(shù)據(jù)分析的第三方公司提供的analytics對象中的purchase函數(shù)。 代碼看起來像這樣

analytics.purchase( purchaseData, function  () {
      payWithYourMoney ()
} );

在這情況下,可能我們會忽略的一個事實是: 我們已經(jīng)把payWithYourMoney 的控制權(quán)完全交給了analytics.purchase函數(shù)了,這讓我們的回調(diào)“任人宰割”,這種控制權(quán)的轉(zhuǎn)移, 被叫做“控制反轉(zhuǎn)”。

然后上線后的一天, 數(shù)據(jù)分析公司的一個隱蔽的bug終于顯露出來, 讓其中一個原本只執(zhí)行一次的payWithYourMoney執(zhí)行了5次, 這讓那個網(wǎng)上商城的客戶極為惱怒, 并投訴了你們公司??赡銈児疽埠軣o奈, 這個時候驚奇的發(fā)現(xiàn): payWithYourMoney的控制完全不在自己的手里 ?。。。?!后來, 為了保證只支付一次, 代碼改成了這樣:

 // 判斷是否已經(jīng)分析(支付)過一次了
var analysisFlag  = true
analytics.purchase( purchaseData, function(){
     if (!analysisFlag) {
           payWithYourMoney ()
           analysisFlag = false
     }
} );

但是, 這種方式雖然巧妙, 但卻仍不夠簡潔優(yōu)雅(后文提到的Promise將改變這一點)。而且, 在回調(diào)函數(shù)的無數(shù)“痛點”中, 它只能規(guī)避掉一個, 如果你嘗試規(guī)避掉所有的“痛點”,代碼將比上面更加復(fù)雜而混亂。

3.太晚調(diào)用或根本沒有調(diào)用

因為你失去了對回調(diào)的控制權(quán), 你的回調(diào)可能會出現(xiàn)預(yù)期之外的過晚調(diào)用或者不調(diào)用的情況(為了處理這個“痛點”你又將混入一些復(fù)雜的代碼邏輯)

4.吞掉報錯

回調(diào)內(nèi)的報錯是可能被包裹回調(diào)的外部函數(shù)捕捉而不報錯,(為了處理這個“痛點”你又又又將混入一些復(fù)雜的代碼邏輯)

5.復(fù)雜情況下可讀性差

請問這段代碼的調(diào)用順序 ?

doA( function(){
    doB();
    doC( function(){
      doD();
          } )
    doE();
} );
doF();

讓人一臉蒙逼的回調(diào)函數(shù)地獄

setTimeout(function (name) {
  var catList = name + ','
  setTimeout(function (name) {
    catList += name + ',';
    setTimeout(function (name) {
      catList += name + ',';
      setTimeout(function (name) {
        catList += name + ',';
        setTimeout(function (name) {
          catList += name;
          console.log(catList);
        }, 1, 'Lion');
      }, 1, 'Snow Leopard');
    }, 1, 'Lynx');
  }, 1, 'Jaguar');}, 1, 'Panther');
6.門

什么叫“門”?, 你可以大概理解成: 現(xiàn)在有一群人準(zhǔn)備進屋,但只有他們所有人都到齊了,才能“進門” ,也就是: 只有所有的異步操作都完成了, 我們才認為它整體完成了,才能進行下一步操作

下面這個例子里, 我們試圖通過兩個異步請求操作,希望當(dāng)a和b的取值都到達的時候才輸出?。?/p>

var a, b;
function foo(x) {
   a = x * 2;
   if (a && b) {
        baz();
    }
}
function bar(y) {
    b = y * 2;
    if (a && b) {
           baz();
    }
}
function baz() {
     console.log( a + b );
}
// ajax(..)是某個庫中的某個Ajax函數(shù)
ajax( "http://some.url.1", foo );
ajax( "http://some.url.2", bar );

這段代碼比前面那段“鏈?zhǔn)健崩锏幕卣{(diào)地獄好懂多了,但是卻依然存在這一些問題:我們使用了兩個 if (a && b) { } 去分別保證baz是在a和b都到達后才執(zhí)行的,試著思考一下:兩個 if (a && b) { } 的判斷條件是否可以合并到一起呢,因為這兩個判斷條件都試圖表達同一種語意: a 和 b都到達, 能合并成一條語句的話豈不是更加簡潔優(yōu)雅 ? (一切都在為Promise做鋪墊哦~~~~啦啦啦)

7.競態(tài)

一組異步操作,其中一個完成了, 這組異步操作便算是整體完成了。在下面,我們希望通過異步請求的方式,取得x的值,然后執(zhí)行foo或者bar,但希望只把foo或者bar其中一個函數(shù)執(zhí)行一次

var flag = true;
function foo(x) {
    if (flag) {
        x = x + 1
        baz(x);
        flag = false
     }
}
function bar(x) {
     if (flag) {
         x = x*2
         baz(x);
         flag = false
     }
}
function baz( x ) {
       console.log( x );
}
// ajax(..)是某個庫中的某個Ajax函數(shù)
ajax( "http://some.url.1", foo );
ajax( "http://some.url.2", bar );

在這里,我們設(shè)置了一個flag, 設(shè)它的初始值為true, 這時候foo或者bar在第一次執(zhí)行的時候, 是可以進入if內(nèi)部的代碼塊并且執(zhí)行baz函數(shù)的, 但在if內(nèi)部的代碼塊結(jié)束的時候, 我們把flag的值置為false,這個時候下一個函數(shù)就無法進入代碼塊執(zhí)行了, 這就是回調(diào)對于競態(tài)的處理。

二、Promise 的含義

Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強大。它由社區(qū)最早提出和實現(xiàn),ES6 將其寫進了語言標(biāo)準(zhǔn),統(tǒng)一了用法,原生提供了Promise對象。

所謂Promise,簡單說就是一個容器,里面保存著某個未來才會結(jié)束的事件(通常是一個異步操作)的結(jié)果。從語法上說,Promise 是一個對象,從它可以獲取異步操作的消息。Promise 提供統(tǒng)一的 API,各種異步操作都可以用同樣的方法進行處理。

Promise對象有以下兩個特點。

(1)對象的狀態(tài)不受外界影響。Promise對象代表一個異步操作,有三種狀態(tài):pending(進行中)、fulfilled(已成功)和rejected(已失?。?。只有異步操作的結(jié)果,可以決定當(dāng)前是哪一種狀態(tài),任何其他操作都無法改變這個狀態(tài)。這也是Promise這個名字的由來,它的英語意思就是“承諾”,表示其他手段無法改變。

(2)一旦狀態(tài)改變,就不會再變,任何時候都可以得到這個結(jié)果。Promise對象的狀態(tài)改變,只有兩種可能:從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected。只要這兩種情況發(fā)生,狀態(tài)就凝固了,不會再變了,會一直保持這個結(jié)果,這時就稱為 resolved(已定型)。如果改變已經(jīng)發(fā)生了,你再對Promise對象添加回調(diào)函數(shù),也會立即得到這個結(jié)果。這與事件(Event)完全不同,事件的特點是,如果你錯過了它,再去監(jiān)聽,是得不到結(jié)果的。

注意,為了行文方便,本章后面的resolved統(tǒng)一只指fulfilled狀態(tài),不包含rejected狀態(tài)。

有了Promise對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調(diào)函數(shù)。此外,Promise對象提供統(tǒng)一的接口,使得控制異步操作更加容易。

Promise也有一些缺點。首先,無法取消Promise,一旦新建它就會立即執(zhí)行,無法中途取消。其次,如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯誤,不會反應(yīng)到外部。第三,當(dāng)處于pending狀態(tài)時,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)。

如果某些事件不斷地反復(fù)發(fā)生,一般來說,使用 Stream 模式是比部署Promise更好的選擇。

三、Promise是怎么解決問題的

1.回調(diào)過早調(diào)用
讓我們回到那個回調(diào)的痛點:我們有可能會寫出一個既可能同步執(zhí)行, 又可能異步執(zhí)行的“zalgo”函數(shù)。但Promise可以自動幫我們避免這個問題:如果對一個 Promise 調(diào)用 then(..) 的時候,即使這個 Promise是立即resolve的函數(shù)(即Promise內(nèi)部沒有ajax等異步操作,只有同步操作), 提供給then(..) 的回調(diào)也是會被異步調(diào)用的,這幫助我們省了不少心

  1. 回調(diào)調(diào)用次數(shù)過多
    Promise 的內(nèi)部機制決定了調(diào)用單個Promise的then方法, 回調(diào)只會被執(zhí)行一次,因為Promise的狀態(tài)變化是單向不可逆的,當(dāng)這個Promise第一次調(diào)用resolve方法, 使得它的狀態(tài)從pending(正在進行)變成fullfilled(已成功)或者rejected(被拒絕)后, 它的狀態(tài)就再也不能變化了。所以你完全不必擔(dān)心Promise.then( function ) 中的function會被調(diào)用多次的情況

  2. 回調(diào)中的報錯被吞掉
    要說明一點的是Promise中的then方法中的error回調(diào)被調(diào)用的時機有兩種情況:

  • a. Promise中主動調(diào)用了reject (有意識地使得Promise的狀態(tài)被拒絕), 這時error回調(diào)能夠接收到reject方法傳來的參數(shù)(reject(error))
  • b. 在定義的Promise中, 運行時候報錯(未預(yù)料到的錯誤), 也會使得Promise的狀態(tài)被拒絕,從而使得error回調(diào)能夠接收到捕捉到的錯誤

例如:

var p = new Promise( function(resolve,reject){
      foo.bar(); // foo未定義,所以會出錯!
      resolve( 42 ); // 永遠不會到達這里 :( 
 } );
p.then(
   function fulfilled(){
       // 永遠不會到達這里 :(    
   },
   function rejected(err){
       // err將會是一個TypeError異常對象來自foo.bar()這一行      
   }
);
  1. 還有一種情況是回調(diào)根本就沒有被調(diào)用,這是可以用Promise的race方法解決(下文將介紹)
// 用于超時一個Promise的工具 
function timeoutPromise(delay) {
   return new Promise( function(resolve,reject){
      setTimeout( function(){
            reject( "Timeout!" );
          }, delay );
      } );
}

// 設(shè)置foo()超時 
Promise.race( [
   foo(), // 試著開始foo()
   timeoutPromise( 3000 ) // 給它3秒鐘
] ).then(
   function(){
     // foo(..)及時完成! 
   },
   function(err){
     // 或者foo()被拒絕,或者只是沒能按時完成
     // 查看err來了解是哪種情況 
   }
);

5.鏈?zhǔn)?br> 我們上面說了, 純回調(diào)的一大痛點就是“金字塔回調(diào)地獄”, 這種“嵌套風(fēng)格”的代碼丑陋難懂,但Promise就可以把這種“嵌套”風(fēng)格的代碼改裝成我們喜聞樂見的“鏈?zhǔn)健憋L(fēng)格。因為then函數(shù)是可以鏈?zhǔn)秸{(diào)用的, 你的代碼可以變成這樣

Promise.then(
  // 第一個異步操作 
).then(
  // 第二個異步操作 
).then(
  // 第三個異步操作
)

6.門Promise.all,競態(tài)Promise.race見后文

四、基本用法

ES6 規(guī)定,Promise對象是一個構(gòu)造函數(shù),用來生成Promise實例。下面代碼創(chuàng)造了一個Promise實例。

const promise = new Promise(function(resolve, reject) {
  // ... some code

  if (/* 異步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
});

Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是resolve和reject。它們是兩個函數(shù),由 JavaScript 引擎提供,不用自己部署。

resolve函數(shù)的作用是,將Promise對象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending 變?yōu)?resolved),在異步操作成功時調(diào)用,并將異步操作的結(jié)果,作為參數(shù)傳遞出去;reject函數(shù)的作用是,將Promise對象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected),在異步操作失敗時調(diào)用,并將異步操作報出的錯誤,作為參數(shù)傳遞出去。

Promise實例生成以后,可以用then方法分別指定resolved狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)。

promise.then(function(value) {
  // success
}, function(error) {
  // failure
});

then方法可以接受兩個回調(diào)函數(shù)作為參數(shù)。第一個回調(diào)函數(shù)是Promise對象的狀態(tài)變?yōu)閞esolved時調(diào)用,第二個回調(diào)函數(shù)是Promise對象的狀態(tài)變?yōu)閞ejected時調(diào)用。其中,第二個函數(shù)是可選的,不一定要提供。這兩個函數(shù)都接受Promise對象傳出的值作為參數(shù)。

下面是一個Promise對象的簡單例子。

function timeout(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms, 'done');
  });
}

timeout(100).then((value) => {
  console.log(value);
});

上面代碼中,timeout方法返回一個Promise實例,表示一段時間以后才會發(fā)生的結(jié)果。過了指定的時間(ms參數(shù))以后,Promise實例的狀態(tài)變?yōu)閞esolved,就會觸發(fā)then方法綁定的回調(diào)函數(shù)。

下面是一個用Promise對象實現(xiàn)的 Ajax 操作的例子。

const getJSON = function(url) {
  const promise = new Promise(function(resolve, reject){
    const handler = function() {
      if (this.readyState !== 4) {
        return;
      }
      if (this.status === 200) {
        resolve(this.response);
      } else {
        reject(new Error(this.statusText));
      }
    };
    const client = new XMLHttpRequest();
    client.open("GET", url);
    client.onreadystatechange = handler;
    client.responseType = "json";
    client.setRequestHeader("Accept", "application/json");
    client.send();

  });

  return promise;
};

getJSON("/posts.json").then(function(json) {
  console.log('Contents: ' + json);
}, function(error) {
  console.error('出錯了', error);
});

上面代碼中,getJSON是對 XMLHttpRequest 對象的封裝,用于發(fā)出一個針對 JSON 數(shù)據(jù)的 HTTP 請求,并且返回一個Promise對象。需要注意的是,在getJSON內(nèi)部,resolve函數(shù)和reject函數(shù)調(diào)用時,都帶有參數(shù)。

五、Promise.prototype.then()

前面說過,then方法的第一個參數(shù)是resolved狀態(tài)的回調(diào)函數(shù),第二個參數(shù)(可選)是rejected狀態(tài)的回調(diào)函數(shù)。

then方法返回的是一個新的Promise實例(注意,不是原來那個Promise實例)。因此可以采用鏈?zhǔn)綄懛ǎ磘hen方法后面再調(diào)用另一個then方法。

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // ...
});

上面的代碼使用then方法,依次指定了兩個回調(diào)函數(shù)。第一個回調(diào)函數(shù)完成以后,會將返回結(jié)果作為參數(shù),傳入第二個回調(diào)函數(shù)。

采用鏈?zhǔn)降膖hen,可以指定一組按照次序調(diào)用的回調(diào)函數(shù)。這時,前一個回調(diào)函數(shù),有可能返回的還是一個Promise對象(即有異步操作),這時后一個回調(diào)函數(shù),就會等待該Promise對象的狀態(tài)發(fā)生變化,才會被調(diào)用。

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function funcA(comments) {
  console.log("resolved: ", comments);
}, function funcB(err){
  console.log("rejected: ", err);
});

上面代碼中,第一個then方法指定的回調(diào)函數(shù),返回的是另一個Promise對象。這時,第二個then方法指定的回調(diào)函數(shù),就會等待這個新的Promise對象狀態(tài)發(fā)生變化。如果變?yōu)閞esolved,就調(diào)用funcA,如果狀態(tài)變?yōu)閞ejected,就調(diào)用funcB。

如果采用箭頭函數(shù),上面的代碼可以寫得更簡潔。

getJSON("/post/1.json").then(
  post => getJSON(post.commentURL)
).then(
  comments => console.log("resolved: ", comments),
  err => console.log("rejected: ", err)
);
六、Promise.prototype.catch()

Promise.prototype.catch方法是.then(null, rejection)的別名,用于指定發(fā)生錯誤時的回調(diào)函數(shù)。

p.then((val) => console.log('fulfilled:', val))
  .catch((err) => console.log('rejected', err));

// 等同于
p.then((val) => console.log('fulfilled:', val))
  .then(null, (err) => console.log("rejected:", err));

下面代碼中,getJSON方法返回一個 Promise 對象,如果該對象狀態(tài)變?yōu)閞esolved,則會調(diào)用then方法指定的回調(diào)函數(shù);如果異步操作拋出錯誤,狀態(tài)就會變?yōu)閞ejected,就會調(diào)用catch方法指定的回調(diào)函數(shù),處理這個錯誤。另外,then方法指定的回調(diào)函數(shù),如果運行中拋出錯誤,也會被catch方法捕獲。

getJSON('/posts.json').then(function(posts) {
  // ...
}).catch(function(error) {
  // 處理 getJSON 和 前一個回調(diào)函數(shù)運行時發(fā)生的錯誤
  console.log('發(fā)生錯誤!', error);
});

用 then 的第二個參數(shù)去處理錯誤不是最好的選擇。因為大多數(shù)情況下我們會用到鏈?zhǔn)秸{(diào)用。類似:promise.then().then().then()。所以在每個 then 方法去處理錯誤顯得代碼很多余,而且也真的沒必要。
catch 方法就是用來做錯誤統(tǒng)一處理。這樣鏈?zhǔn)秸{(diào)用中我們只需要用 then 來處理 fulfilled 狀態(tài),在鏈的末尾加上 catch 來統(tǒng)一處理錯誤。

new Promise((resolve, reject) => {
setTimeout(() => {        
          resolve(100);    
    }, 2000)
}).then(result => {    
    return result * num // 這里模擬一個錯誤,num 未定義
}).then(result => {    
    return result / 2;
}).catch(err => {    
    console.log(err); // num is not defined
})

這里舉得例子比較簡單,在 then 方法里面沒有去 return 新的 promise??梢钥吹降谝粋€ then 發(fā)生了錯誤,最后的 catch 會捕捉這個錯誤。catch 實際上是.then(null, rejection)的別名。

七、Promise.prototype.finally()

finally方法用于指定不管 Promise 對象最后狀態(tài)如何,都會執(zhí)行的操作。該方法是 ES2018 引入標(biāo)準(zhǔn)的。

promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});

上面代碼中,不管promise最后的狀態(tài),在執(zhí)行完then或catch指定的回調(diào)函數(shù)以后,都會執(zhí)行finally方法指定的回調(diào)函數(shù)。

下面是一個例子,服務(wù)器使用 Promise 處理請求,然后使用finally方法關(guān)掉服務(wù)器。

server.listen(port)
  .then(function () {
    // ...
  })
  .finally(server.stop);

finally方法的回調(diào)函數(shù)不接受任何參數(shù),這意味著沒有辦法知道,前面的 Promise 狀態(tài)到底是fulfilled還是rejected。這表明,finally方法里面的操作,應(yīng)該是與狀態(tài)無關(guān)的,不依賴于 Promise 的執(zhí)行結(jié)果。

八、Promise.all()

試想一個頁面聊天系統(tǒng),我們需要從兩個不同的URL分別獲得用戶的個人信息和好友列表,這兩個任務(wù)是可以并行執(zhí)行的,用Promise.all()實現(xiàn)如下:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
// 同時執(zhí)行p1和p2,并在它們都完成后執(zhí)行then:
Promise.all([p1, p2]).then(function (results) {
    console.log(results); // 獲得一個Array: ['P1', 'P2']
});
九、Promise.race()

有些時候,多個異步任務(wù)是為了容錯。比如,同時向兩個URL讀取用戶的個人信息,只需要獲得先返回的結(jié)果即可。這種情況下,用Promise.race()實現(xiàn):

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});

由于p1執(zhí)行較快,Promise的then()將獲得結(jié)果'P1'。p2仍在繼續(xù)執(zhí)行,但執(zhí)行結(jié)果將被丟棄。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Promise的含義: ??Promise是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和...
    呼呼哥閱讀 2,270評論 0 16
  • title: promise總結(jié) 總結(jié)在前 前言 下文類似 Promise#then、Promise#resolv...
    JyLie閱讀 12,416評論 1 21
  • 如果瀏覽已經(jīng)有了Promise對象,那么頁就說明瀏覽器的js引擎里已經(jīng)有了Promsise隊列,這樣就可以利用Pr...
    羊烊羴閱讀 579評論 0 0
  • 1.Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理...
    不可不信緣_b32e閱讀 320評論 0 1
  • 2018年7月第3本 《目標(biāo)》 主題:時間管理 字?jǐn)?shù):未知 閱讀時間:60+30分鐘 作者用工廠的生產(chǎn)作為具體的示...
    CarolLiao閱讀 181評論 0 0

友情鏈接更多精彩內(nèi)容