1. 程式人生 > >AsyncCallback AsyncCallback 非同步回撥委託

AsyncCallback AsyncCallback 非同步回撥委託

 

AsyncCallback 非同步回撥委託

AsyncCallback

意義:

非同步操作完成時呼叫的方法

語法1:

構造非同步回撥物件

AsyncCallback 非同步回撥物件名asyncCallback = new AsyncCallback(非同步操作完成時呼叫的方法MyAsyncCallback);

 

語法2:

定義委託,並進行非同步呼叫,非同步呼叫完成後自動觸發

委託型別Action fn委託名 = Run委託定義;
委託名fn.BeginInvoke(非同步回撥物件名asyncCallback );

 

示例:

複製程式碼
using System;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class UseDelegateForAsyncCallback
    {
        delegate string MethodDelegate(int iCallTime);
        public static void Main()
        {
            MethodDelegate dlgt = (m)=> 
            {
                return "你輸入的數字是"+m;
            };
            AsyncCallback asyncCallback = new AsyncCallback(MyAsyncCallback);           
            Action fn = Run;
            fn.BeginInvoke(asyncCallback,null);

        }

        private static void MyAsyncCallback(IAsyncResult ar)
        {
            Console.WriteLine("非同步呼叫");
            Console.ReadLine();
        }


        public static void Run()
        {

        }
    }
}
///  控制檯輸出結果:
///     你輸入的數字是:5
///     非同步呼叫

JS非同步程式設計

JS三座大山:原型原型鏈、作用域閉包、同步非同步。
之前有寫過自己對閉包的理解,今天來總結一下JS中的非同步。

思考(案例來自stackoverflow):

function foo(){
    var result; $ajax({ url:'...', success:function(response){ result=response; //return response;//tried this one as well } }); return result; } var result=foo();

初學非同步的時候,這裡是很容易錯的地方,你想要獲取從伺服器端返回的資料,結果卻一直undefined。
分析:


JavaScript是單執行緒語言,但是js中有很多工耗時比較長,比如ajax請求,如果都按照順序進行,往往會出現瀏覽器無響應的情況,所以就需要非同步的形式。JS中所有的任務可以分為兩種:同步任務和非同步任務。

同步任務:在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;

非同步任務:不進入主執行緒,而進入任務佇列中的任務,只有任務佇列通知主執行緒,某個非同步任務可以執行了,這個任務才會進入主執行緒執行。

事件迴圈(Event Loop):只有執行棧中的所有同步任務都執行完畢,系統才會讀取任務佇列,看看裡面的非同步任務哪些可以執行,然後那些對應的非同步任務,結束等待狀態,進入執行棧,開始執行。


非同步的解決方案:

下面我們嘗試將上面程式碼改正一下,幾種方法如下:
1.callback

function foo(callback){//定義函式的時候將另一個函式(回撥函式)作為引數傳入定義的函式中。 $ajax({ //... success:callback//非同步操作執行完畢後,再執行該回調函式,確保回撥在非同步操作之後執行。 }); } function myCallback(result){ //... } foo(myCallback);

回撥函式本身是我們約定俗成的一種叫法,我們定義它,但是並不會自己去執行它,它最終被其他人執行了。

優點:比較容易理解;
缺點:1.高耦合,維護困難,回撥地獄;2.每個任務只能指定一個回撥函式;3.如果幾個非同步操作之間並沒有順序之分,同樣也要等待上一個操作執行結束再進行下一個操作。下圖回撥地獄(圖片來自於新浪微博(@ruanyf)):

圖片描述

2.Promise

function ajax(url){
    return new Promise(function(resolve,reject){ var xhr=new XMLHttpRequest(); xhr.onload=function(){ resolve(this.responseText); }; xhr.onerror=reject; xhr.open('GET',url); xhr.send(); }); } ajax('/echo/json') .then(function(result){...}) .then(function(){...}) .catch(function(){...});

ES6給我們提供了一個原生的建構函式Promise,Promise代表了一個非同步操作,可以將非同步物件和回撥函式脫離開來,通過.then方法在這個非同步操作上繫結回撥函式,Promise可以讓我們通過鏈式呼叫的方法去解決回撥巢狀的問題,而且由於promise.all這樣的方法存在,可以讓同時執行多個操作變得簡單。

promise物件存在三種狀態:
1)Fulfilled:成功狀態
2)Rejected:失敗狀態
3)Pending:既不是成功也不是失敗狀態,可以理解為進行中狀態

promise物件的兩個重要方法:resolve/reject
1)resolve方法可以使Promise物件的狀態改變為成功,同時傳遞一個引數用於後續成功後的操作。
2)reject方法可以將Promise物件的狀態改變為失敗,同時將錯誤資訊傳遞到後續錯誤處理的操作。

.then可以使用鏈式呼叫,原因在於:每一次執行該方法時總會返回一個Promise物件。
另外,在then的函式當中的返回值,可以作為後續操作的引數(例如:.then(return a).then(console.log(a+b)))

那麼問題來了,如果上面程式碼非同步操作丟擲錯誤,會怎麼樣?會呼叫catch方法指定的回撥函式,處理這個錯誤,而且then方法指定的回撥函式,如果執行中丟擲錯誤,也會被catch捕獲。Promise物件的錯誤具有“冒泡”性質,會一直向後傳遞,直到被捕獲為止,也就是說,錯誤總是會被下一個catch語句捕獲。

理解Promise用法的關鍵點:
1.then方法是Promise例項的方法,即Promise.prototype上的,它的作用是為Promise例項新增狀態改變時的回撥函式,這個方法的第一個引數是resolved狀態的回撥函式,第二個引數(可選)是rejected狀態的回撥函式。
2.鏈式中的第二個then開始,它們的resolve中的引數,是前一個then中resolve的return語句的返回值。
3.關於執行順序:Promise在例項化的時候就會執行,也就是如果Promise的例項化語句中函式console.log輸出語句,它會比then中的先執行。Promise.all中傳入的Promise物件的陣列(假設為p1、p2),即使p2的執行速度比p1快,Promise.all方法仍然會按照陣列中的順序將結果返回。
理解了上面這些方便寫原生的Promise,利用觀察者模式。後面補充。

Promise的缺點:
1.當處於未完成狀態時,無法確定目前處於哪一階段。
2.如果不設定回撥函式,Promise內部的錯誤不會反映到外部。
3.無法取消Promise,一旦新建它就會立即執行,無法中途取消。

3.async/await:

很多人說async/await是非同步程式設計的終極解決方案、
JavaScript 的 async/await 實現,離不開 Promise。

var superagent=require('superagent')
function delay(){ return new Promise(function(resolve,reject){ setTimeout({ resolve(42); },3000); }) } async function getAllBooks(){ var bookIDs=await superagent.get('/user/books'); await delay(1000); return await superagent.get('/books/ids='JSON.stringify(bookIDs)); } getAllBooks() .then(function(){});

上面的 delay() 沒有申明為 async。實際上,delay() 本身就是返回的 Promise 物件,加不加 async 結果都一樣。

只要在函式名之前加上async關鍵字,就表明這個函式內部有非同步操作。這個非同步操作返回一個Promise物件,前面用await關鍵字註明。函式執行的時候,一旦遇到await,就會先執行await後面的表示式中的內容(非同步),不再執行函式體後面的語句。等到非同步操作執行完畢後,再自動返回到函式體內,繼續執行函式體後面的語句。

async:定義非同步函式
1)自動把函式轉換為Promise
2)當呼叫非同步函式時,函式返回值會被resolve處理
3)非同步函式內部可以使用await

await:暫停非同步函式的執行
1)當使用在Promise前面時,await等待Promise完成,並返回Promise的結果
2)await只能和Promise一起使用,不能和callback一起使用
3)await只能用在async函式中

async/await並不會取代promise,因為async/await底層依然使用promise。

async function getABC(){ let A = await getValueA(); // getValueA 花費 2 秒 let B = await getValueB(); // getValueA 花費 4 秒 let C = await getValueC(); // getValueA 花費 3 秒 return A*B*C }

每次遇到 await 關鍵字時,Promise 都會停下在,一直到執行結束,所以總共花費是 2+4+3 = 9 秒。await 把非同步變成了同步。

async function getABC() { // Promise.all() 允許同時執行所有的非同步函式 let results = await Promise.all([ getValueA, getValueB, getValueC ]); return results.reduce((total,value) => total * value); } 

函式總耗時為 4 秒(getValueB 的耗時)。

Async 的價值在於用寫同步的方式寫非同步,1避免了阻塞,2必免寫回調

 

AsyncCallback

意義:

非同步操作完成時呼叫的方法

語法1:

構造非同步回撥物件

AsyncCallback 非同步回撥物件名asyncCallback = new AsyncCallback(非同步操作完成時呼叫的方法MyAsyncCallback);

 

語法2:

定義委託,並進行非同步呼叫,非同步呼叫完成後自動觸發

委託型別Action fn委託名 = Run委託定義;
委託名fn.BeginInvoke(非同步回撥物件名asyncCallback );

 

示例:

複製程式碼
using System;

namespace Examples.AdvancedProgramming.AsynchronousOperations
{
    public class UseDelegateForAsyncCallback
    {
        delegate string MethodDelegate(int iCallTime);
        public static void Main()
        {
            MethodDelegate dlgt = (m)=> 
            {
                return "你輸入的數字是"+m;
            };
            AsyncCallback asyncCallback = new AsyncCallback(MyAsyncCallback);           
            Action fn = Run;
            fn.BeginInvoke(asyncCallback,null);

        }

        private static void MyAsyncCallback(IAsyncResult ar)
        {
            Console.WriteLine("非同步呼叫");
            Console.ReadLine();
        }


        public static void Run()
        {

        }
    }
}
///  控制檯輸出結果:
///     你輸入的數字是:5
///     非同步呼叫

JS非同步程式設計

JS三座大山:原型原型鏈、作用域閉包、同步非同步。
之前有寫過自己對閉包的理解,今天來總結一下JS中的非同步。

思考(案例來自stackoverflow):

function foo(){
    var result; $ajax({ url:'...', success:function(response){ result=response; //return response;//tried this one as well } }); return result; } var result=foo();

初學非同步的時候,這裡是很容易錯的地方,你想要獲取從伺服器端返回的資料,結果卻一直undefined。
分析:
JavaScript是單執行緒語言,但是js中有很多工耗時比較長,比如ajax請求,如果都按照順序進行,往往會出現瀏覽器無響應的情況,所以就需要非同步的形式。JS中所有的任務可以分為兩種:同步任務和非同步任務。

同步任務:在主執行緒上排隊執行的任務,只有前一個任務執行完畢,才能執行後一個任務;

非同步任務:不進入主執行緒,而進入任務佇列中的任務,只有任務佇列通知主執行緒,某個非同步任務可以執行了,這個任務才會進入主執行緒執行。

事件迴圈(Event Loop):只有執行棧中的所有同步任務都執行完畢,系統才會讀取任務佇列,看看裡面的非同步任務哪些可以執行,然後那些對應的非同步任務,結束等待狀態,進入執行棧,開始執行。


非同步的解決方案:

下面我們嘗試將上面程式碼改正一下,幾種方法如下:
1.callback

function foo(callback){//定義函式的時候將另一個函式(回撥函式)作為引數傳入定義的函式中。 $ajax({ //... success:callback//非同步操作執行完畢後,再執行該回調函式,確保回撥在非同步操作之後執行。 }); } function myCallback(result){ //... } foo(myCallback);

回撥函式本身是我們約定俗成的一種叫法,我們定義它,但是並不會自己去執行它,它最終被其他人執行了。

優點:比較容易理解;
缺點:1.高耦合,維護困難,回撥地獄;2.每個任務只能指定一個回撥函式;3.如果幾個非同步操作之間並沒有順序之分,同樣也要等待上一個操作執行結束再進行下一個操作。下圖回撥地獄(圖片來自於新浪微博(@ruanyf)):

圖片描述

2.Promise

function ajax(url){
    return new Promise(function(resolve,reject){ var xhr=new XMLHttpRequest(); xhr.onload=function(){ resolve(this.responseText); }; xhr.onerror=reject; xhr.open('GET',url); xhr.send(); }); } ajax('/echo/json') .then(function(result){...}) .then(function(){...}) .catch(function(){...});

ES6給我們提供了一個原生的建構函式Promise,Promise代表了一個非同步操作,可以將非同步物件和回撥函式脫離開來,通過.then方法在這個非同步操作上繫結回撥函式,Promise可以讓我們通過鏈式呼叫的方法去解決回撥巢狀的問題,而且由於promise.all這樣的方法存在,可以讓同時執行多個操作變得簡單。

promise物件存在三種狀態:
1)Fulfilled:成功狀態
2)Rejected:失敗狀態
3)Pending:既不是成功也不是失敗狀態,可以理解為進行中狀態

promise物件的兩個重要方法:resolve/reject
1)resolve方法可以使Promise物件的狀態改變為成功,同時傳遞一個引數用於後續成功後的操作。
2)reject方法可以將Promise物件的狀態改變為失敗,同時將錯誤資訊傳遞到後續錯誤處理的操作。

.then可以使用鏈式呼叫,原因在於:每一次執行該方法時總會返回一個Promise物件。
另外,在then的函式當中的返回值,可以作為後續操作的引數(例如:.then(return a).then(console.log(a+b)))

那麼問題來了,如果上面程式碼非同步操作丟擲錯誤,會怎麼樣?會呼叫catch方法指定的回撥函式,處理這個錯誤,而且then方法指定的回撥函式,如果執行中丟擲錯誤,也會被catch捕獲。Promise物件的錯誤具有“冒泡”性質,會一直向後傳遞,直到被捕獲為止,也就是說,錯誤總是會被下一個catch語句捕獲。

理解Promise用法的關鍵點:
1.then方法是Promise例項的方法,即Promise.prototype上的,它的作用是為Promise例項新增狀態改變時的回撥函式,這個方法的第一個引數是resolved狀態的回撥函式,第二個引數(可選)是rejected狀態的回撥函式。
2.鏈式中的第二個then開始,它們的resolve中的引數,是前一個then中resolve的return語句的返回值。
3.關於執行順序:Promise在例項化的時候就會執行,也就是如果Promise的例項化語句中函式console.log輸出語句,它會比then中的先執行。Promise.all中傳入的Promise物件的陣列(假設為p1、p2),即使p2的執行速度比p1快,Promise.all方法仍然會按照陣列中的順序將結果返回。
理解了上面這些方便寫原生的Promise,利用觀察者模式。後面補充。

Promise的缺點:
1.當處於未完成狀態時,無法確定目前處於哪一階段。
2.如果不設定回撥函式,Promise內部的錯誤不會反映到外部。
3.無法取消Promise,一旦新建它就會立即執行,無法中途取消。

3.async/await:

很多人說async/await是非同步程式設計的終極解決方案、
JavaScript 的 async/await 實現,離不開 Promise。

var superagent=require('superagent')
function delay(){ return new Promise(function(resolve,reject){ setTimeout({ resolve(42); },3000); }) } async function getAllBooks(){ var bookIDs=await superagent.get('/user/books'); await delay(1000); return await superagent.get('/books/ids='JSON.stringify(bookIDs)); } getAllBooks() .then(function(){});

上面的 delay() 沒有申明為 async。實際上,delay() 本身就是返回的 Promise 物件,加不加 async 結果都一樣。

只要在函式名之前加上async關鍵字,就表明這個函式內部有非同步操作。這個非同步操作返回一個Promise物件,前面用await關鍵字註明。函式執行的時候,一旦遇到await,就會先執行await後面的表示式中的內容(非同步),不再執行函式體後面的語句。等到非同步操作執行完畢後,再自動返回到函式體內,繼續執行函式體後面的語句。

async:定義非同步函式
1)自動把函式轉換為Promise
2)當呼叫非同步函式時,函式返回值會被resolve處理
3)非同步函式內部可以使用await

await:暫停非同步函式的執行
1)當使用在Promise前面時,await等待Promise完成,並返回Promise的結果
2)await只能和Promise一起使用,不能和callback一起使用
3)await只能用在async函式中

async/await並不會取代promise,因為async/await底層依然使用promise。

async function getABC(){ let A = await getValueA(); // getValueA 花費 2 秒 let B = await getValueB(); // getValueA 花費 4 秒 let C = await getValueC(); // getValueA 花費 3 秒 return A*B*C }

每次遇到 await 關鍵字時,Promise 都會停下在,一直到執行結束,所以總共花費是 2+4+3 = 9 秒。await 把非同步變成了同步。

async function getABC() { // Promise.all() 允許同時執行所有的非同步函式 let results = await Promise.all([ getValueA, getValueB, getValueC ]); return results.reduce((total,value) => total * value); } 

函式總耗時為 4 秒(getValueB 的耗時)。

Async 的價值在於用寫同步的方式寫非同步,1避免了阻塞,2必免寫回調