JavaScript從初級往高級走系列————異步

原文博客地址:https://finget.github.io/2018/05/21/async/

異步

  • 什麼是單線程,和異步有什麼關係
  • 什麼是event-loop
  • 是否用過jQuery的Deferred
  • Promise的基本使用和原理
  • 介紹一下async/await(和Promise的區別、聯繫)
  • 異步解決方案

什麼是單線程,和異步有什麼關係

單線程-只有一個線程,只作一件事。JS之因此是單線程,取決於它的實際使用,例如JS不可能同添加一個DOM和刪除這個DOM,因此它只能是單線程的。
console.log(1);
alert(1);
console.log(2);

上面這個例子中,當執行了alert(1),若是用戶不點擊肯定按鈕,console.log(2)是不會執行的。javascript

爲了利用多核CPU的計算能力,HTML5提出 WebWorker標準,容許JavaScript腳本建立多個線程,可是子線程徹底受主線程控制,且不得操做DOM。因此,這個新標準並無改變JavaScript單線程的本質。

js異步

console.log(100);
setTimeout(function(){
  console.log(200);
},1000)
console.log(300);
console.log(400);
console.log(400);
.... // 這裏來不少不少個console.log(400); 結果就是打印完全部的400,等一秒再打印200

event-loop

event-loop

文字解釋

  • 事件輪詢,JS實現異步的具體解決方案
  • 同步代碼,直接執行
  • 異步函數先放在異步隊列中
  • 待同步函數執行完畢,輪詢執行 異步隊列 的函數

上面那個例子的執行效果就是這樣的:

實例分析:

前端

這個例子中有兩種狀況,取決於ajax的返回時間,若是ajax時間小於100ms它就先放進異步隊列

Jquery Deferred

Jquery1.5先後的變化

var ajax = $.ajax({
  url: 'data.json',
  success: function(){
    console.log('success1');
    console.log('success2');
    console.log('success3');
  },
  error: function(){
    console.log('error');
  }
})

console.log(ajax); // 返回一個xhr對象
// 鏈式操做
var ajax = $.ajax('data.json');
ajax.done(function(){
  console.log('success1');
}).fail(function(){
  console.log('error');
}).done(function(){
  console.log()
})

console.log(ajax); // 返回一個deferred對象
  • 沒法改變JS異步和單線程的本質
  • 只能從寫法上杜絕callback這種形式
  • 它是一種語法糖形式,可是解耦了代碼
  • 很好的體現:開放封閉原則(對擴展開放,對修改封閉)

使用Jquery Deferred

// 給出一段很是簡單的異步操做代碼,使用setTimeout函數
var wait = function(){
  var task = function(){
    console.log('執行完成)
  }
  setTimeout(task, 2000);
}
wait();

新增需求:要在執行完成以後進行某些特別複雜的操做,代碼可能會不少,並且分好幾個步驟java

function waitHandle(){
  var dtd = $.Deferred(); // 建立一個deferred對象
  
  var wait = function(dtd){ // 要求傳入一個deferred對象
    var task = function(){
      console.log('執行完成');
      dtd.resolve(); // 表示異步任務已經完成
      // dtd.reject(); // 表示異步任務失敗或出錯
    }
    setTimeout(task, 2000);
    return dtd; // 要求返回deferred對象
  }
  
  // 注意,這裏必定要有返回值
  return wait(dtd);
}

var w = waitHandle();
w.then(function(){
  console.log('ok 1');
}, function(){
  console.log('err 1');
}).then(function(){
  console.log('ok 2');
}, function(){
  console.log('err 2');
})

當執行dtd.reject()時:git

var w = waitHandle();
w.then(function(){
  console.log('ok 1');
}, function(){
  console.log('err 1');
})
// 不能鏈式
w.then(function(){
  console.log('ok 2');
}, function(){
  console.log('err 2');
})

上面封裝的waitHandle方法,因爲直接返回了dtd(deferred對象),因此用戶能夠直接調用w.reject()方法,致使不管是成功仍是失敗,最後都走失敗。github

// 修改
function waitHandle(){
  var dtd = $.Deferred();
  var wait = function(dtd){
    var task = function(){
      console.log('執行完成');
      dtd.resolve(); 
    }
    setTimeout(task, 2000);
    return dtd.promise(); // 注意這裏返回的是promise,而不是直接返回deferred對象
  }
  return wait(dtd);
}
ES6的Promise: 點這裏
// promise封裝一個異步加載圖片的方法
function loadImg(src) {
  var promise = new Promise(function(resolve,reject){
    var img = document.createElement('img');
    img.onload = function(){
      resolve(img)
    }
    img.onerror = function(){
      reject('圖片加載失敗')
    }
    img.src = src;
  })
  return promise;
}

async/await

這是ES7提案中的,如今babel已經開始支持了,koa也是用async/await實現的。
  • then 只是將callback拆分了
  • async/await 是最直接的同步寫法
// 僞代碼
const load = async function(){
  const result1 = await loadImg(src1);
  console.log(result1);
  const result2 = await loadImg(src2);
  console.log(result2);
}
load();

最後

建立了一個前端學習交流羣,感興趣的朋友,一塊兒來嗨呀!
ajax

相關文章
相關標籤/搜索