精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

JavaScript異步之從回調函數到Promise

原創
開發 前端
JavaScript的異步處理是前端工程師必須接觸的一塊內容。ES6在JavaScript異步的處理上引入了新的特性,使得程序員能夠更加優雅地處理異步問題。

[[250903]]

【51CTO.com原創稿件】 JavaScript的異步處理是前端工程師必須接觸的一塊內容。ES6在JavaScript異步的處理上引入了新的特性,使得程序員能夠更加優雅地處理異步問題。

若您想通過本教程直接上手Promise,那么請按順序閱讀。

若您只是想了解Promise概念,那么請直接閱讀每章的***小節,等需要的時候,再回過頭來看具體的例子,從而不至于浪費您太多時間。

1.基于回調函數

1.1.異步動作與回調函數

在JavaScript中往往需要處理很多異步動作(asynchronous actions),如后臺請求某個dashboard上的顯示數據、響應一條定時信息。異步動作的執行不會阻塞其他動作,且在執行完成之后,由回調函數(callback)處理異步動作的結果。

假如你想載入一個JavaScript 腳本,并在腳本載入完畢之后調用一個回調函數來完成載入之后的操作。代碼片1.1-1實現了這樣一個異步函數loadScript。

代碼片1.1-1 

  1. //src代表JavaScript 腳本的URL,callback代表自定義回調函數 
  2. function loadScript(src, callback) { 
  3.   let script = document.createElement('script'); 
  4.   script.src = src;  
  5.   script.onload = () => callback(script);  
  6.   document.head.append(script); 
  7.  
  8.  loadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => { 
  9.   alert(`Cool, the ${script.src} is loaded`); 
  10.   alert( _ ); // function declared in the loaded script 
  11. });  

***一行loadScript的調用可以讀作:異步函數loadScript載入腳本,并在載入執行完畢后調用毀掉函數彈出提示框。

1.2.異步動作的順序執行

很多場景下,往往需要依次執行多個異步動作(上一個異步動作結束之后才能執行下一個)。通過結合回調函數,可以寫成一個“嵌套”的異步函數,如下1.2-1。

代碼片1.2-1 

  1. loadScript('/my/script.js'function(script) {  
  2.   loadScript('/my/script2.js'function(script) {  
  3.     loadScript('/my/script3.js'function(script) { 
  4.       // ...continue after all scripts are loaded 
  5.     });  
  6.   })  
  7. });  

1.3.異步動作的異常處理

在實際場景中,還需要根據異步函數的執行狀態(正?;蛘弋惓?來執行不同的回調函數。代碼片1.3-1是代碼片1.1-1中的改進版本,通過增加對onerror事件的響應,異步動作拋出的異常能由用戶提供的函數來接管。此時要注意的是,這里的回調函數與前面不同,它是形式為function(error,script)的函數。

代碼片1.3-1 

  1. function loadScript(src, callback) { 
  2.   let script = document.createElement('script'); 
  3.   script.src = src; 
  4.  
  5.   script.onload = () => callback(, script); // 1 
  6.   script.onerror = () => callback(new Error(`Script load error for ${src}`)); // 2 
  7.  
  8.   document.head.append(script); 
  9.  
  10. loadScript('/my/script.js'function(error, script) { 
  11.   if (error) { 
  12.     // handle error 
  13.   } else { 
  14.     // script loaded successfully 
  15.   } 
  16. });  

這里loadScript的調用可以讀作:異步函數loadScript載入腳本,并在腳本載入執行失敗后調用2,在腳本載入執行成功后調用1。

1.4. 異步動作帶來的問題——惡魔金字塔

結合1.2和1.3,可以得到一個包含異常處理和多個異步動作順序執行的例子,如代碼片1.4-1所示。

代碼片1.4-1 

  1. loadScript('1.js'function(error, script) {  
  2.   if (error) { 
  3.     handleError(error); 
  4.   } else { 
  5.     // ... 
  6.     loadScript('2.js'function(error, script) { 
  7.       if (error) { 
  8.         handleError(error); 
  9.       } else { 
  10.         // ... 
  11.         loadScript('3.js'function(error, script) { 
  12.           if (error) { 
  13.             handleError(error); 
  14.           } else { 
  15.             // ...continue after all scripts are loaded (*) 
  16.           } 
  17.         }); 
  18.  
  19.       } 
  20.     }) 
  21.   } 
  22. });  

可以看到,隨著嵌套的深入,從左往右看代碼就形成了一個金字塔結構的嵌套。這樣得到的代碼非常不利于維護和拓展,因此也被稱為惡魔金字塔(Pyramid of doom)。

代碼片1.4-2解決了惡魔金字塔的問題,但也引入了可讀性和命名空間的問題,因此不算一個優雅的解決方案。

代碼片1.4-2 

  1. loadScript('1.js', step1);  
  2. function step1(error, script) { 
  3.   if (error) { 
  4.     handleError(error); 
  5.   } else { 
  6.     // ... 
  7.     loadScript('2.js', step2); 
  8.   } 
  9.  
  10. function step2(error, script) { 
  11.   if (error) { 
  12.     handleError(error); 
  13.   } else { 
  14.     // ... 
  15.     loadScript('3.js', step3); 
  16.   } 
  17.  
  18. function step3(error, script) { 
  19.   if (error) { 
  20.     handleError(error); 
  21.   } else { 
  22.     // ...continue after all scripts are loaded (*) 
  23.   } 
  24. };  

2.基于Promise

2.1.Promise是什么

Promise是為了解決回調函數的一些缺陷而在ES6中定義的異步解決方案,它的訂閱模式與鏈式表達式能讓開發者更加方便的定義自己的異步動作。

為了更好的理解Promise想要解決的問題,可以想象這樣一個場景:想象你是一個知名歌手,你的粉絲問你單曲發售的消息。你讓他們訂閱你的消息,這樣在你準備好專輯之后,就有專人負責通知你的粉絲,讓他們獲取關于單曲的信息,好讓他們購買專輯并推薦給身邊的朋友。

這里”歌手發布一首單曲”就是一個異步動作的生產代碼(producing code)(實際中可能是向服務器請求一條數據),“粉絲接受單曲發售的通知,然后購買專輯并推薦給身邊的朋友”,這一動作就是消費代碼(consuming code)(類似回調函數),而連接兩者的“專人”就是Promise。

Promise是一個JavaScript對象,它將生產代碼和消費代碼聯系起來,從而在生產代碼完成異步動作后,訂閱異步動作的消費代碼就能獲取結果(假如初次接觸Promise,到這至少已經理解一半了。但想了解如何使用Promise或者想閱讀Promise相關的代碼,你還得繼續)。

2.2.生成一個Promise對象

根據2.1可知,Promise起到的就是“橋接”生產代碼和消費代碼的作用。Promise對象通過傳入一個執行器(executor)執行生產代碼,消費代碼通過.then和.catch方法訂閱結果(生產代碼的結果可能是正常的返回值也可能是一個異常)。理解了生產代碼的傳入和消費代碼如何訂閱結果,也就明白了Promise的用法。

2.2.1.生產代碼

Promise對象通過傳入一個執行器(executor)執行生產代碼。執行器是形式為function(resolve, reject)的函數,它包含了異步動作的生產代碼。執行器會在Promise對象創建的時候自動執行。當執行器執行完成任務之后,會調用resolve(解析)來接受異步動作正常執行完畢的結果,調用reject(拒絕)來接受一個在異步動作中拋出的異常(Error)。

這樣可能還是不夠直觀,那就看看代碼片2.2.1-1,它利用Promise改造了代碼片1.3-1。onload(表示腳本正常載入完畢)和onerror(載入過程中拋出異常)兩個異步狀態分別執行了resolve和reject方法,分別接受一個DOM對象和Error對象。若生產代碼調用resolve解析,則Promise會把DOM對象作為結果通知給消費代碼;反之若調用reject方法,則Promise把Error對象作為結果通知給消費代碼。

代碼片2.2.1-1 

  1. function loadScript(src) { 
  2.   return new Promise(function(resolve, reject) { 
  3.     let script = document.createElement('script'); 
  4.     script.src = src; 
  5.  
  6.     script.onload = () => resolve(script); 
  7.     script.onerror = () => reject(new Error("Script load error: " + src)); 
  8.  
  9.     document.head.append(script); 
  10.   }); 
  11.  

Promise如何能夠得知一個異步狀態?這是因為Promise對象維護了兩個重要內部屬性:

  • state(狀態) :初始是“pending”,執行完畢之后變化成“fulfilled”或者“rejected”。
  • result(結果):異步動作的結果值??梢匀我庵付?,默認是undefined。

當調用resolve時設置state為fulfilled,并把result作為參數傳給resolve;當調用reject時設置state為rejected,并把result作為參數傳給rejected。從邏輯上來看,rejected和resolve可以看做是異步動作結果的”容器”,一旦state改變,Promise就從“容器”中取出result并通知消費代碼處理。

2.2.2.消費代碼

.then和.catch方法可以使消費代碼能夠接受Promise對象發送的消息,訂閱生產代碼的結果。

2.2.2.1..then方法

.then方法的強大之處在于它的靈活性,可以定義兩個函數接受分別接受resolve和reject返回的結果。代碼片2.2.2.1-1和代碼片2.2.2.1-2分別反映了.then方法對resolve和reject結果的不同的響應。

代碼片2.2.2.1-1 

  1. let promise = new Promise(function(resolve, reject) { 
  2.   setTimeout(() => resolve("done!"), 1000); 
  3. }); 
  4.  
  5. // resolve runs the first function in .then 
  6. promise.then
  7.   result => alert(result), // shows "done!" after 1 second 
  8.   error => alert(error) // doesn't run 
  9. );  

代碼片2.2.2.1-2 

  1. let promise = new Promise(function(resolve, reject) { 
  2.   setTimeout(() => reject(new Error("Whoops!")), 1000); 
  3. }); 
  4.  
  5. // reject runs the second function in .then 
  6. promise.then
  7.   result => alert(result), // doesn't run 
  8.   error => alert(error) // shows "Error: Whoops!" after 1 second 
  9. );  

若.then方法只傳入了一個參數,那么默認消費代碼只訂閱resovle接受的結果,如代碼片2.2.2.1-3所示。

代碼片2.2.2.1-3 

  1. let promise = new Promise(resolve => { 
  2.   setTimeout(() => resolve("done!"), 1000); 
  3. }); 
  4.  
  5. promise.then(alert); // shows "done!" after 1 second  

2.2.2.2..catch方法

若消費代碼想單獨捕獲異常(訂閱異常結果),可以考慮使用.catch。.catch是.then(null,alert)的一個快捷方式。代碼片2.2.2.2-1是這兩種的實現方式的例子。

代碼片2.2.2.2-1 

  1. let promise = new Promise((resolve, reject) => { 
  2.   setTimeout(() => reject(new Error("Whoops!")), 1000); 
  3. }); 
  4.  
  5. // .catch(f) is the same as promise.then(null, f) 
  6. promise.catch(alert); // shows "Error: Whoops!" after 1 second 
  7.  
  8. // .catch(f) is the same as promise.then(null, f) 
  9. promise.then(,alert); // shows "Error: Whoops!" after 1 second  

2.3.使用Promise需要注意的一些細節

2.3.1.一個執行器只會執行一次resolve或者reject

在代碼片2.3.1-1的執行器中,除了***個resolve之外的其他resolve或者reject都會被忽略。這兩個方法中的額外參數也會被忽略。

代碼片2.3.1-1 

  1. let promise = new Promise(function(resolve, reject) { 
  2.   resolve("done"); 
  3.  
  4.   reject(new Error("…")); // ignored 
  5.   setTimeout(() => resolve("…")); // ignored 
  6. });  

2.3.2.使用Error對象或者繼承自Error類的對象作為reject的參數

這是一個好的實踐,這樣能對異常進行更好的處理(比如針對不通的異常類型進行不同的操作)。

2.3.3.立即執行resolve/reject

雖然在實際中,執行器往往執行一些異步操作,但是你也可以在執行器中立刻執行resolve或者reject方法,這完全沒有關系。這樣你的結果會被直接投遞到消費代碼。如代碼片2.4.3-1所示。

代碼片2.3.3-1 

  1. let promise = new Promise(function(resolve, reject) { 
  2.   // not taking our time to do the job 
  3.   resolve(123); // immediately give the result: 123 
  4. });  

2.3.4..then和.catch中定義的handler都是異步的

.then和.catch中定義的handler都是異步的,這意味著即使Promise立刻執行了到了resolve或者reject,handler也必須等待當前的代碼執行完畢才能被加載,如代碼片2.3.4-1所示。雖然執行器立即執行了resolve得到了結果,但是.then(alert)也在***被調用。如代碼片2.3.4-1所示。

代碼片2.3.4-1 

  1. // an "immediately" resolved Promise 
  2. const executor = resolve => resolve("done!"); 
  3. const promise = new Promise(executor);  
  4. promise.then(alert); // this alert shows last (*)  
  5. alert("code finished"); // this alert shows first  

3.Promise鏈

在實際中,很多時候往往需要順序執行異步任務,但是用也帶來了”惡魔金字塔”的問題(如1.4節描述)。引入Promise鏈,我們可以優雅的解決這個問題。

3.1.Promise鏈中的.then

多個.then方法可以構成一條Promise鏈。代碼片3.1.-1就是一個簡單的例子。

代碼片3.1.-1 

  1. new Promise(function(resolve, reject) {  
  2.   setTimeout(() => resolve(1), 1000); // (*)  
  3. }).then(function(result) { // (**)  
  4.   alert(result); // 1 
  5.   return result * 2;  
  6. }).then(function(result) { // (***)  
  7.   alert(result); // 2 
  8.   return result * 2; 
  9.  
  10. }).then(function(result) {  
  11.   alert(result); // 4 
  12.   return result * 2;  
  13. });  

執行該代碼,結果為1——2——4,這是因為.then返回一個Promise方法,并隱式地把值賦給了Promise對象的result屬性,使得***個Promise的result屬性能夠通過調用鏈不斷傳遞。

倘若想在.then中包含異步操作,則必須返回一個包含異步對象的Promise。在處理異步操作期間,Promise鏈上的handler均不會執行,待異步操作完成,才將結果傳遞到鏈的下一個節點。

代碼片3.1.-2 

  1. new Promise(function(resolve, reject) {  
  2.   setTimeout(() => resolve(1), 1000);  
  3. }).then(function(result) {  
  4.   alert(result); // 1  
  5.   return new Promise((resolve, reject) => { // (*) 
  6.     setTimeout(() => resolve(result * 2), 1000); 
  7.   }); 
  8.  
  9. }).then(function(result) { // (**)  
  10.   alert(result); // 2  
  11.   return new Promise((resolve, reject) => { 
  12.     setTimeout(() => resolve(result * 2), 1000); 
  13.   }); 
  14.  
  15. }).then(function(result) {  
  16.   alert(result); // 4  
  17. });  

在代碼片3.1.-2中,***的結果也是1——2——4,但是每個alter都相隔1s才會顯示??梢岳斫鉃閞eturn一個Promise阻礙了結果的傳播,必須要等這個異步動作結束,結果才能在Promise鏈中繼續傳遞。

3.2.Promise鏈中的.catch

.catch可以對Promise鏈中的異常進行處理??紤]代碼片3.2.-1。假設我們引入fetch函數(用來獲取json)獲取用戶的頭像(avatar)并顯示,.catch可以捕獲該Promise鏈中拋出的異常。

代碼片3.2.-1 

  1. fetch('/article/promise-chaining/user.json'
  2.   .then(response => response.json()) 
  3.   .then(user => fetch(`https://api.github.com/users/${user.name}`)) 
  4.   .then(response => response.json()) 
  5.   .then(githubUser => new Promise(function(resolve, reject) { 
  6.     let img = document.createElement('img'); 
  7.     img.src = githubUser.avatar_url; 
  8.     img.className = "promise-avatar-example"
  9.     document.body.append(img); 
  10.  
  11.     setTimeout(() => { 
  12.       img.remove(); 
  13.       resolve(githubUser); 
  14.     }, 3000); 
  15.   })) 
  16.   .catch(error => alert(error.message));  

但是這樣還不夠好,在實際編碼中,常常需要在代碼中拋出異常,并根據異常的類型來做相應的處理。幸運的是,Promise鏈默認把在處理鏈中拋出的異常當reject進行處理,并讓用戶用.catch捕獲。如代碼片3.2.-2所示,loadJson函數在Promise鏈中會檢測HTTP的狀態碼,若不為200(不成功),就拋出自定義異常“new HttpError(response)”,并被catch所捕獲。

代碼片3.2.-2 

  1. class HttpError extends Error { // (1) 
  2.   constructor(response) { 
  3.     super(`${response.status} for ${response.url}`); 
  4.     this.name = 'HttpError'
  5.     this.response = response; 
  6.   } 
  7.  
  8. function loadJson(url) { // (2) 
  9.   return fetch(url) 
  10.     .then(response => { 
  11.       if (response.status == 200) { 
  12.         return response.json(); 
  13.       } else { 
  14.         throw new HttpError(response); 
  15.       } 
  16.     }) 
  17.  
  18. loadJson('no-such-user.json') // (3) 
  19.   .catch(alert); // HttpError: 404 for .../no-such-user.json  

3.3.重新拋出異常及未處理異常

在一般的try…catch…結構中,若一個異常無法處理,往往可以重新拋出(Rethrowing)給上一級的異常處理函數處理。Promise鏈也支持這種形式。在Promis中也可以重新拋出異常,并被最近一個.catch所捕獲。

考慮代碼片3.3.-1。在Promise對象拋出一個異常”Whoops!”之后,這個Promise對象的狀態變為拒絕(reject),鏈上最近的一個.catch方法被調用,并判斷是否是URI異常,顯然”Whoops!”不屬于這類異常,因此顯示”Can’t handle such error”,并重新拋出異常。該異常被鏈上的第二個.catch所捕獲,最終顯示”The unknown error has occurred: Error: Whoops!”。

代碼片3.3.-1 

  1. // the execution: catch -> catch -> then 
  2. new Promise(function(resolve, reject) { 
  3.  
  4.   throw new Error("Whoops!"); 
  5.  
  6. }).catch(function(error) { // (*) 
  7.  
  8.   if (error instanceof URIError) { 
  9.     // handle it 
  10.   } else { 
  11.     alert("Can't handle such error"); 
  12.  
  13.     throw error; // throwing this or another error jumps to the next catch 
  14.   } 
  15.  
  16. }).then(function() { 
  17.   /* never runs here */ 
  18. }).catch(error => { // (**) 
  19.  
  20.   alert(`The unknown error has occurred: ${error}`); 
  21.   // don't return anything => execution goes the normal way 
  22.  
  23. });  

一般來說Promise鏈底部寫上.catch來捕獲異常是一個非常好的習慣。假如不這樣做,那么javaScript引擎會捕獲該異常并在控制臺顯示 。當然,也可以在瀏覽器中可以通過注冊一個unhandledrejection事件(unhandledrejection事件是HTML標準的一部分)監聽器,來捕獲未處理異常,如代碼片3.3.-2所示:

代碼片3.3.-2 

  1. window.addEventListener('unhandledrejection'function(event) { 
  2.   // the event object has two special properties: 
  3.   alert(event.promise); // [object Promise] - the promise that generated the error 
  4.   alert(event.reason); // Error: Whoops! - the unhandled error object 
  5. }); 
  6.  
  7. new Promise(function() { 
  8.   throw new Error("Whoops!"); 
  9. }); // no catch to handle the er  

4.Promise API

Promise對象有四個靜態方法:resolve/reject/all/race,可以在某些場景下讓處理Promise對象的代碼變得更加簡潔。

4.1.Promise.resolve/Promise.reject

Promise.resolve/Promise.reject直接返回一個已經被resolve/reject的Promise對象。代碼片4.1-1和代碼片4.1-2分別顯示了Promise.resolve和Promise.reject的等價形式。值得注意的是,Promise.resolve/Promise.reject返回的是Promise對象,因此也可用.then/.catch構成Promise鏈。

代碼片4.1-1 

  1. let promise = Promise.resolve(value); 
  2. let promise = new Promise(resolve => resolve(value)); 
  3. 代碼片4.1-2  

代碼片4.1-2 

  1. let promise = Promise.reject(error);  
  2. let promise = new Promise((resolve, reject) => reject(error));  

4.2.Promise.all

Promise.all接受一個可迭代對象(往往是Promise數組)作為輸入,并行地執行它們,等待所有Promise執行完畢之后返回一個Promise對象。這個Promise對象的result屬性是包含所有對應結果的一個數組,如代碼片4.2-1所示。

代碼片4.2-1 

  1. Promise.all([ 
  2.   new Promise((resolve, reject) => setTimeout(() => resolve(1), 3000)), // 1 
  3.   new Promise((resolve, reject) => setTimeout(() => resolve(2), 2000)), // 2 
  4.   new Promise((resolve, reject) => setTimeout(() => resolve(3), 1000))  // 3 
  5. ]).then(alert); // 1,2,3 when promises are ready: each promise contributes an array member  

需要指出的是,當傳入的可迭代對象中包含非Promise對象的元素時,Promise.all會自動調用Promise.resolve方法將其包裝成一個Promise對象并返回。如代碼片4.2-2所示。

代碼片4.2-2 

  1. Promise.all([ 
  2.   new Promise((resolve, reject) => { 
  3.     setTimeout(() => resolve(1), 1000) 
  4.   }), 
  5.   2, // treated as Promise.resolve(2) 
  6.   3  // treated as Promise.resolve(3) 
  7. ]).then(alert); // 1, 2, 3  

4.3.Promise.race

Promise.race接受一個可迭代對象(往往是Promise數組)作為輸入,并行地執行它們,將***個返回的Promise對象作為結果,如代碼片4.3-1所示。***alter的結果是1

代碼片4.3-1 

  1. Promise.race([ 
  2.   new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)), 
  3.   new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)), 
  4.   new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)) 
  5. ]).then(alert); // 1  

5.async/await

假如你是按順序讀完,那么到這里理解async/await關鍵字就非常容易。async/await關鍵字作為語法糖,能讓操作Promise的代碼更加簡潔可讀。

5.1.async

async關鍵詞置于你想修飾的函數前,可以將一個非Promise的結果通過Promise.resolve的封裝變成一個Promise對象,如代碼片5.1-1所示。

代碼片5.1-1 

  1. async function f() { 
  2.   return 1; 
  3.  
  4. f().then(alert); // 1 
  5. 5.2.await  

5.2.await

await的作用和.then非常相似,用來等待一個Promise對象的異步返回。await和async密不可分,await必須在async修飾的函數中才能使用。如代碼片5.2-1所示。

代碼片5.2-1 

  1. async function f() { 
  2.  
  3.   let promise = new Promise((resolve, reject) => { 
  4.     setTimeout(() => resolve("done!"), 1000) 
  5.   }); 
  6.  
  7.   let result = await promise; // wait till the promise resolves (*) 
  8.  
  9.   alert(result); // "done!" 
  10.  
  11. f();  

值得注意的是,一旦使用await,就可以使用try…catch來捕獲異常。相比.catch來說,這樣捕獲異常更加方便。

代碼片5.2-2 

  1. async function f() { 
  2.  
  3.   try { 
  4.     let response = await fetch('http://no-such-url'); 
  5.   } catch(err) { 
  6.     alert(err); // TypeError: failed to fetch 
  7.   } 
  8.  
  9. f();  

6.總結

本文參考在線教程并根據個人的實踐經驗有側重的總結了一下ES6的異步特性:Promise概念、基本用法、靜態方法以及兩個關鍵字async和await。這里沒有提到的是,Promise仍然有著一些缺點,比如它無法像RxJS一般很好地處理流事件。和所有的教程一樣,本文不可能涵蓋到異步編程的所有細節,但是若能對你有所啟發,那就是再好不過了。

7. 參考鏈接

8.作者簡介

邱仁博,多年運營商商業分析、數據中心數據庫方向工作經驗,現任職于某地市事業單位信息技術部。日常關注國內外極客新聞、前后端技術。海外知識搬運工。

【51CTO原創稿件,合作站點轉載請注明原文作者和出處為51CTO.com】

 

責任編輯:龐桂玉 來源: 51CTO
相關推薦

2019-11-05 10:03:08

callback回調函數javascript

2017-05-11 20:20:59

JavascriptPromiseWeb

2021-06-07 09:44:10

JavaScript開發代碼

2021-12-10 07:47:30

Javascript異步編程

2025-03-24 07:20:00

2021-01-14 07:52:24

JavaScript回調函數

2017-08-28 15:21:29

異步處理回調函數異步編程

2021-06-06 19:51:07

JavaScript異步編程

2015-10-26 09:25:42

2009-08-21 17:02:20

ASP.NET異步回調

2023-06-29 07:48:35

異步加載JavaScript

2023-11-08 13:18:00

JestJavaScript框架

2016-09-18 21:14:54

JavascriptPromiseWeb

2012-02-01 10:33:59

Java

2023-08-23 13:24:00

異步編程方法

2022-01-04 20:52:50

函數異步Promise

2009-11-09 15:58:07

WCF回調方法

2023-04-28 15:20:37

JavaScript事件循環

2022-04-12 08:30:52

回調函數代碼調試

2011-07-25 14:32:40

Cocoa 框架 函數
點贊
收藏

51CTO技術棧公眾號

亚洲精品免费在线| 久色成人在线| 亚洲国产精品久久久久| a√天堂在线观看| 免费国产在线观看| 精品一区二区三区影院在线午夜| 欧美日韩成人在线观看| 中文字幕免费高清视频| 看片一区二区| 性做久久久久久免费观看| 欧洲亚洲一区| 黄色成人一级片| 日韩二区三区在线观看| 欧美高清自拍一区| 日本美女bbw| 国产精品久久久久久久久久白浆 | 小泽玛利亚一区| japanese色系久久精品| 欧美视频一区二区三区在线观看| 免费人成在线观看视频播放| 9色在线观看| av成人免费在线观看| 成人免费视频在线观看超级碰| 国产精品第一页在线观看| 欧美性生交xxxxx久久久| 欧美最近摘花xxxx摘花| 少妇久久久久久被弄高潮| 欧美综合精品| 日韩一级片网站| 亚洲国产精品三区| 一区二区精品伦理...| 亚洲韩国精品一区| 一区二区三区一级片| 日韩欧美在线观看一区二区| 成人免费观看视频| 91视频-88av| 最近中文字幕在线观看| 亚洲综合激情| 97婷婷大伊香蕉精品视频| 日韩黄色免费观看| 久久精品播放| 在线日韩中文字幕| 添女人荫蒂视频| 精品伊人久久久| 欧美成人一级视频| 丰满少妇中文字幕| 国产精品久久久久久久久久久久久久久| 精品久久久久人成| 99在线精品免费视频| 99视频免费在线观看| 中文字幕色av一区二区三区| 亚洲aⅴ天堂av在线电影软件| 你懂的在线观看| 久久久久久9999| 欧美二区三区在线| 日本激情一区二区三区| 成人精品在线视频观看| 亚洲综合小说区| 亚洲第一免费视频| 国产精品系列在线观看| 99久re热视频这里只有精品6| 精品人妻av一区二区三区| 国产一区三区三区| av免费精品一区二区三区| 亚洲国产成人一区二区| 成人午夜私人影院| 精品亚洲第一| 国产一级片在线播放| 中文字幕国产一区二区| 色中文字幕在线观看| 成人在线免费看黄| 亚洲国产va精品久久久不卡综合| 黄色免费福利视频| 欧美黑人疯狂性受xxxxx野外| 色欧美日韩亚洲| 高清av免费看| 亚洲一二av| 日韩成人在线视频| 亚洲天堂久久新| 久久精品不卡| 久久久久久国产精品三级玉女聊斋 | 黄色一级片免费看| 欧美专区在线| 国产日韩综合一区二区性色av| 国产精品主播一区二区| 成人涩涩免费视频| 欧美综合激情| av毛片在线免费看| 欧美日韩国产区| 天天干天天爽天天射| 欧洲精品99毛片免费高清观看| 精品少妇一区二区三区日产乱码 | 日本精品久久久久久久| 中文字幕久久久久| 国产suv精品一区二区883| 牛人盗摄一区二区三区视频| 精产国品自在线www| 午夜精品久久久久久久蜜桃app| 欧美在线观看视频网站| 香蕉成人app| 亚洲一区999| 国产真实乱偷精品视频| 日本亚洲欧美天堂免费| 俄罗斯精品一区二区三区| 精品一二三区视频| 亚洲一区二区三区四区中文字幕| 亚洲高清在线免费观看| av成人资源网| zzijzzij亚洲日本成熟少妇| 超碰超碰超碰超碰| 国产精品一区二区三区99| 日韩国产精品一区二区| 久久亚洲导航| 8x8x8国产精品| 色婷婷在线影院| 欧美日韩亚洲三区| 国产精品一区二区性色av| 午夜小视频免费| 亚洲欧美日韩中文播放| 欧美自拍小视频| 日韩精品福利一区二区三区| 欧美日本精品在线| 在线视频1卡二卡三卡| 91啪九色porn原创视频在线观看| 日本福利视频在线观看| 青青久久精品| 这里只有视频精品| 欧美a视频在线观看| 成人一二三区视频| 三级在线免费观看| 亚洲ww精品| 在线视频国产日韩| 黄色污污网站在线观看| 91毛片在线观看| 久久综合色视频| 91麻豆精品激情在线观看最新 | 性欧美18xxxhd| 精品国产一区久久| 青青操视频在线播放| 韩国精品一区二区| 一区二区视频在线播放| 嫩草伊人久久精品少妇av杨幂| 亚洲欧美中文日韩v在线观看| 国产精品xxxx喷水欧美| 成人av中文字幕| 久久成人福利视频| 国产成人精品亚洲线观看| 欧美日韩xxxxx| 亚洲高清精品视频| 亚洲影视在线播放| 亚洲麻豆一区二区三区| 亚洲国产第一| 久久久久久艹| 精品3atv在线视频| 一区二区三区四区在线观看视频| 成人午夜精品视频| 国产片一区二区| 91制片厂毛片| 婷婷激情综合| 草莓视频一区| 99riav视频在线观看| 国产视频一区在线| 精品无码一区二区三区的天堂| 国产亚洲精品久| 手机看片一级片| 欧美91视频| 国产另类第一区| 一本大道色婷婷在线| 亚洲午夜未删减在线观看| 亚洲永久精品一区| 亚洲视频一区在线| 高清中文字幕mv的电影| 久久久久99| 亚洲最大免费| 999久久久久久久久6666| 97色在线视频观看| 成人精品一区二区三区校园激情| 欧美精品v日韩精品v韩国精品v| 欧美交换国产一区内射| 9l国产精品久久久久麻豆| 日本三区在线观看| 2023国产精品久久久精品双| 成人动漫在线视频| 在线精品亚洲欧美日韩国产| 日韩在线观看高清| 黄色av免费观看| 欧美体内she精视频| 综合五月激情网| 91色九色蝌蚪| 午夜大片在线观看| 国产欧美亚洲一区| 一区二区三区四区五区视频| 国产精品对白久久久久粗| 国产精品久久电影观看| 男女免费观看在线爽爽爽视频| 亚洲日韩第一页| 精品国产亚洲AV| 色猫猫国产区一区二在线视频| 午夜精品福利在线视频| 91亚洲精品乱码久久久久久蜜桃 | 日韩中文字幕视频| 欧美熟妇乱码在线一区| 欧美日韩国产综合一区二区| 国产成人无码精品久在线观看| 国产精品你懂的在线欣赏| 国产51自产区| 久草这里只有精品视频| 无码人妻h动漫| 欧美久久影院| 一区二区精品在线| 亚洲永久精品唐人导航网址| 国产高清自拍99| 亚洲日日夜夜| 国产精品白丝jk喷水视频一区| av中文在线资源| 久久天堂电影网| 福利在线播放| 国产视频在线观看一区二区| 亚洲国产成人一区二区| 91精品国产一区二区三区| 欧美亚洲另类小说| 精品福利在线观看| 99热精品免费| 1000部国产精品成人观看| 少妇人妻好深好紧精品无码| www.亚洲人| 亚洲精品久久一区二区三区777 | 欧美激情办公室videoshd| 亚洲欧美激情四射在线日| 好男人www在线视频| 欧美一区二区女人| 国产精品高潮呻吟AV无码| 欧美日韩中文字幕一区二区| 国产精品久久久久久人| 五月激情六月综合| 国产性xxxx高清| 亚洲成av人片一区二区梦乃| 国产一级特黄a高潮片| 亚洲综合色网站| 欧美激情精品久久| 一区二区三区在线视频观看58| 永久免费看片视频教学| 国产精品三级av| 欧美视频一区二区在线| 国产精品久久综合| 99自拍偷拍视频| 国产精品电影一区二区| 黄色片网站在线播放| 中文字幕在线不卡视频| 亚洲女人久久久| 亚洲三级免费观看| 日韩视频中文字幕在线观看| 亚洲乱码精品一二三四区日韩在线| 老熟妻内射精品一区| 亚洲精品亚洲人成人网在线播放| 欧美亚洲日本在线| 亚洲影院在线观看| 老妇女50岁三级| 亚洲一区二区三区不卡国产欧美| 国产无码精品在线观看| 欧美日韩国产精品一区二区不卡中文 | 欧美性xxxx在线播放| av图片在线观看| 在线精品国精品国产尤物884a| 一级片在线免费播放| 欧美日韩在线播放| 国产精品视频第一页| 日韩欧美国产综合在线一区二区三区| 丰满肥臀噗嗤啊x99av| 亚洲国产日韩欧美在线图片| 欧美日韩国产中文字幕在线| 国产亚洲xxx| 国产精品一区二区三区视频网站| 久久久久久18| 九九九伊在线综合永久| 91精品国产自产在线老师啪| 97青娱国产盛宴精品视频| 久久综合给合久久狠狠色| av亚洲免费| 国产91视频一区| 久久精品官网| 国产精品自拍视频在线| 高清在线观看日韩| 国产精品毛片一区二区| 中文字幕日韩欧美一区二区三区| 久草国产在线视频| 一本一道久久a久久精品综合蜜臀| 亚洲一区精品在线观看| 精品电影一区二区| 高清毛片在线看| 久久久久国产精品一区| 日韩经典一区| 国产精品日本一区二区| 欧洲毛片在线视频免费观看| av动漫在线播放| 免费一区二区视频| www国产视频| 亚洲欧洲中文日韩久久av乱码| 国产三级av片| 欧美岛国在线观看| 高清av电影在线观看| 午夜精品久久久久久99热| 国外成人福利视频| 精品国产综合久久| 久久久久国产精品| 国产精品97在线| 国产一区二区精品久久99| 免费看污片网站| 亚洲福利一区二区三区| 一级特黄aa大片| 亚洲欧美另类人妖| 免费在线中文字幕| 成人网址在线观看| 国产一区二区三区站长工具| 久久久久久www| 国产一区二区在线看| 免费黄在线观看| 欧美性猛交xxxx| 婷婷色在线视频| 色综合天天狠天天透天天伊人| 成人污版视频| 亚洲国产欧美日韩| 久久一区亚洲| 亚洲国产精品成人综合久久久| 亚洲激情图片小说视频| 一本色道久久综合熟妇| 伊人青青综合网站| 成人福利av| 精品国产福利| 亚洲无毛电影| 精品无码av一区二区三区| 亚洲色图欧洲色图| 96亚洲精品久久久蜜桃| 中文字幕日韩有码| 国产一区二区主播在线| 品久久久久久久久久96高清| 99热精品在线观看| www.日本高清| 欧美日韩国产一区二区三区| 欧美 中文字幕| 91精品国产乱码久久久久久久久 | 一本一道久久a久久精品逆3p | 91久久国产综合久久| 四虎精品在永久在线观看| 97人人模人人爽人人喊中文字| 农村少妇一区二区三区四区五区| 亚洲国产成人精品无码区99| 成人av电影在线播放| 日韩熟女精品一区二区三区| 亚洲福利在线播放| а√天堂8资源在线| 国产日韩欧美精品| 亚洲美女毛片| www.日本高清| 色狠狠综合天天综合综合| 九色在线视频| 成人精品久久一区二区三区| 午夜免费一区| 久久人妻少妇嫩草av蜜桃| 亚洲国产视频在线| 午夜视频在线播放| 国产精品v片在线观看不卡| 人人狠狠综合久久亚洲婷| 看看黄色一级片| 一区二区三区在线观看国产| 午夜精品久久久久久久91蜜桃| 久久久久久一区二区三区 | 国产乱码精品一区二区三区忘忧草 | 一区二区在线观看视频在线观看| 亚洲xxx在线| 欧美一区二区影院| 成人免费在线观看av| 中文字幕12页| 午夜久久久久久久久| 日本国产在线| 91久久嫩草影院一区二区| 精品96久久久久久中文字幕无| 少妇毛片一区二区三区| 欧美午夜影院一区| 在线观看中文| 国产欧美日韩综合一区在线观看| 久久久久国产精品一区二区| 香蕉久久久久久久| 精品国产免费一区二区三区四区| 亚洲精品mv| 国产av第一区| 91年精品国产| 国产巨乳在线观看| 性欧美xxxx视频在线观看| 凹凸成人精品亚洲精品密奴| 久久久国产精品久久久| 色狠狠综合天天综合综合| 在线网址91| 日韩av一级大片| 粉嫩aⅴ一区二区三区四区| 日韩中文字幕高清| 欧美成人免费全部观看天天性色| 亚洲理论电影| 日本人妻一区二区三区| 欧美中文字幕一区二区三区|