本文講述了異步流程的演變過程。那麼是什麼是異步編程呢: 簡單來說就是執行一個指令不會立刻返回結果而執行下一個任務,而是等到特定的事件觸發後,才能獲得結果。javascript
咱們知道 javascript 運行在瀏覽器中,以 Google 瀏覽器爲例子, v8 引擎,包含 內存堆: 這是內存分配發生的地方。 調用棧: 這是你代碼執行的地方。html
運行一個函數時,解析器把該函數添加到棧中而且執行這個函數。
Web APIs: DOM、AJAX、Timeout(setTimeout)
js是一門單線程的語言, 這意味這它只有一個調用棧。
當咱們堆棧執行的函數須要大量時間時,瀏覽器會中止響應,幸運的是咱們有異步回調。
javaScript引擎 運行在宿主環境中(瀏覽器或者 node),
CallbackQueue and Event Loop
事件循環和回調隊列
調用棧和回調隊列,當棧爲空時,它會調取出隊列中的第一個事件,放到調用棧中執行;
常見的 macro-task(這個隊列也被叫作 task queue) 好比: setTimeout、setInterval、 setImmediate、script(總體代碼)、 I/O 操做、UI 渲染等。
常見的 micro-task 好比: process.nextTick、Promise、Object.observe、MutationObserver 等。
promise 永遠會在隊列尾部添加微觀任務
爲何Promise的代碼(microtask)會比setTimeout的代碼(macrotask)更優先執行,由於它太機智了,居然會插隊!
複製代碼
一雙能敲代碼的手、一臺能執行代碼的電腦。 須要預先引入的庫java
const fs = require('fs')
const co = require('co')
const util = require('util')
複製代碼
第一階段:回調函數node
function readFile(cb) { fs.readFile('./package.json', (err, data) => { if (err) return cb(err) cb(null, data) }) } readFile((err, data) => { if (!err) { data = JSON.parse(data) console.log(data.name) } }) 複製代碼
回調函數的弊端:jquery
採用了事件驅動模型,任務的執行不取決與代碼的順序,取決於某個事件是否發生。編程
function f1() { setTimeout(function() { // f1的任務代碼 f1.trigger('done'); }, 1000); } 複製代碼
假定咱們存在一個任務中心,當某個事件完成以後,咱們就發射狀態信號,調度中心能夠通知訂閱了該狀態信號的其餘任務。這個也稱爲觀察者模式。json
jQuery.subscribe("done", f2); function f1(){ setTimeout(function () { // f1的任務代碼 jQuery.publish("done"); }, 1000); } 當f1 執行完成後, 向信號中心"jquery"發佈"done"信號,從而引起f2的執行。 複製代碼
第二階段:Promisepromise
定義階段:promise(resolve, reject)分別成功或者失敗時處理什麼。瀏覽器
調用階段:經過then函數實現,成功就執行resolve,它會將reslove的值傳遞給最近的then函數,做爲then函數的參數。若是出錯reject,那麼交給catch來捕獲異常markdown
promise的要點以下:
將回調函數中的結果延後到 then 函數裏處理或交給全局異常處理
咱們約定將每一個函數的返回值都得是 promise 對象。 只要是 promise 對象, 就能夠控制狀態並支持 then 方法,將無限個 promise 對象連接在一塊兒。
hello('xx.html').then(log).then(function() { return world('./xxx.js').then(log) }).catch(err => { console.log(err) }) 複製代碼
每一個 promise 對象都有 then 方法, 也就是說then方法是定義在原型對象promise.prototype上的, 它的做用是爲 promise 實例添加狀態改變時的回調函數
Promise.prototype.then() = function (success, fail) { this.done(success) this.fail(fail) return this } 複製代碼
通常狀況下,只傳 success 回調函數便可,fail函數可選,使用catch來捕獲函數異常比經過fail函數進行處理更加可控。
const requireDirectory = require(require-directory ) module.export = requireDirectory(module) function readFileAsync(path) { return new Promise((resolve, reject) => { fs.readFile(path, (err, data) => { if (err) reject(err) else resolve(data) }) }) } readFileAsync('./package.json') .then(data => { data = JSON.parse(data) console.log(data.name) }) .catch(err => { console.log(err) }) 複製代碼
咱們來看看下面這個例子:
new Promise(function (resolve) { resolve(1) }).then(function (value) { console.log(value) }) Promise.resolve(1).then(function (value) { console.log(' Promise.resovle' + value) }) var error = new Error('this is a error') 複製代碼
new Promise更爲強大,Promise.resolve更爲便捷
如下是更爲便捷的寫法
function hello(i){ return Promise.resolve(i) } hello(1).then(function(){ console.log('promise.reslove1=' + value) }) // Promise.resolve返回的是prmise對象,至關於 new Promise(resolve,reject)實例 // Promise.prototype.then()方法的語法以下 p.then(onFulfilled,onRejected); // p.then(function(value)){} p.catch(onRejected) // p.catch(function(reson)) {} 複製代碼
//整個promise 還有這種寫法 hell('./xx.json').then(function (data) { return new Promise(function (reslove, reject) { console.log('promise ' + data) reslove(data) }) }).then(function (data) { return new Promise(function (reslove, reject) { console.log('promise ' + data) reslove(data) }) }).then(function (data) { return new Promise(function (reslove, reject) { console.log('promise ' + data) reslove(data) }) }).catch(function (err) { console.log(err) }) 複製代碼
const PENDING = "pending" const FULFILLED = "fulfilled" const REJECTED = "rejected" function MyPromise(executor){ // executor:這是實例Promise對象時在構造器中傳入的參數,通常是一個function(resolve,reject){} let that = this; this.status = PENDING; this.value = undefined; this.reason = undefined; this.onFulfilledCallbacks = []; this.onRejectedCallbacks = []; function resolve(value){ if(value instanceof Promise) { return value.then(resolve, reject); } // 要確保 onFulfilled 和 onRejected 方法異步執行 setTimeout(() => { // 調用resolve 回調對應onFulfilled函數 if (that.status === PENDING) { // 只能由pedning狀態 => fulfilled狀態 (避免調用屢次resolve reject) that.status = FULFILLED; that.value = value; that.onFulfilledCallbacks.forEach(cb => cb(that.value)); } }); } function reject(reason){ setTimeout(() => { // 調用reject 回調對應onRejected函數 if (that.status === PENDING) { // 只能由pedning狀態 => rejected狀態 (避免調用屢次resolve reject) that.status = REJECTED; that.reason = reason; that.onRejectedCallbacks.forEach(cb => cb(that.reason)); } }); } // executor方法可能會拋出異常,須要捕獲 try { executor(resolve, reject); } catch (e) { reject(e); } } MyPromise.prototype.then = function(onFulfilled, onRejected) { //獲取下this let self = this; if(this.status === FULFILLED){ onFulfilled(self.value) } if(this.status === REJECTED){ onRejected(self.value) } //異步時處理 if(this.status === PENDING){ //保存回調函數 this.onFulfilledCallbacks.push(()=>{ onFulfilled(self.value) }); this.onRejectedCallbacks.push(()=>{ onRejected(self.reason) }) } }; var mp = new MyPromise((resolve, reject) => { console.log(11111); setTimeout(() => { resolve(22222); console.log(3333); }, 1000); }); mp.then(x => { console.log(x); console.log('4444') }, (err) => { console.log('err2', err); }) 複製代碼
上述代碼就是一個簡單的promise了,可是還有兩點問題沒有解決1.鏈式調用 2.不傳值時。 咱們改造下
MyPromise.prototype.then = function(onFulfilled, onRejected) { //獲取下this let that = this; //鏈式調用 在建立一個promise let promsie2 = null; //解決onFulfilled、onRejected沒有傳值的問題 onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err} if (that.status === FULFILLED) { // 成功態 return newPromise = new MyPromise((resolve, reject) => { setTimeout(() => { try{ let x = onFulfilled(that.value); resolvePromise(newPromise, x, resolve, reject); // 新的promise resolve 上一個onFulfilled的返回值 } catch(e) { reject(e); // 捕獲前面onFulfilled中拋出的異常 then(onFulfilled, onRejected); } }); }) } if (that.status === REJECTED) { // 失敗態 return newPromise = new MyPromise((resolve, reject) => { setTimeout(() => { try { let x = onRejected(that.reason); resolvePromise(newPromise, x, resolve, reject); } catch(e) { reject(e); } }); }); } if (that.status === PENDING) { // 等待態 // 將onFulfilled/onRejected收集暫存到集合中 return newPromise = new MyPromise((resolve, reject) => { that.onFulfilledCallbacks.push((value) => { try { let x = onFulfilled(value); resolvePromise(newPromise, x, resolve, reject); } catch(e) { reject(e); } }); that.onRejectedCallbacks.push((reason) => { try { let x = onRejected(reason); resolvePromise(newPromise, x, resolve, reject); } catch(e) { reject(e); } }); }); } }) }; 複製代碼
resolvePromsie 是什麼呢: Promise A+ 2.27規範
/** * resolve中的值幾種狀況: * 1.普通值 * 2.promise對象 * 3.thenable對象/函數 */ /** * 對resolve 進行改造加強 針對resolve中不一樣值狀況 進行處理 * @param {promise} promise2 promise1.then方法返回的新的promise對象 * @param {[type]} x promise1中onFulfilled的返回值 * @param {[type]} resolve promise2的resolve方法 * @param {[type]} reject promise2的reject方法 */ function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { // 若是從onFulfilled中返回的x 就是promise2 就會致使循環引用報錯 return reject(new TypeError('循環引用')); } let called = false; // 避免屢次調用 // 若是x是一個promise對象 (該判斷和下面 判斷是否是thenable對象重複 因此無關緊要) if (x instanceof Promise) { // 得到它的終值 繼續resolve if (x.status === PENDING) { // 若是爲等待態需等待直至 x 被執行或拒絕 並解析y值 x.then(y => { resolvePromise(promise2, y, resolve, reject); }, reason => { reject(reason); }); } else { // 若是 x 已經處於執行態/拒絕態(值已經被解析爲普通值),用相同的值執行傳遞下去 promise x.then(resolve, reject); } // 若是 x 爲對象或者函數 } else if (x != null && ((typeof x === 'object') || (typeof x === 'function'))) { try { // 是不是thenable對象(具備then方法的對象/函數) let then = x.then; if (typeof then === 'function') { then.call(x, y => { if(called) return; called = true; resolvePromise(promise2, y, resolve, reject); }, reason => { if(called) return; called = true; reject(reason); }) } else { // 說明是一個普通對象/函數 resolve(x); } } catch(e) { if(called) return; called = true; reject(e); } } else { resolve(x); } } 複製代碼
測試代碼
var p1 = new MyPromise(function (resolve) { setTimeout(function () { resolve(1); }, 1000); }) p1.then(function (val) { console.log(val) var p3 = new MyPromise(function (resolve) { setTimeout(function () { resolve(val + 1); }, 1000); }); return p3; }).then(function (val) { console.log(val); var p4 = new MyPromise(function (resolve) { setTimeout(function () { resolve(val + 1); }, 1000); }); return p4 }).then(function (val){ console.log(val); var p4 = new MyPromise(function (resolve) { setTimeout(function () { resolve(val + 1); }, 1000); }); }); 複製代碼
增長Promise.resolve、catch、race 方法
// 用於promise方法鏈時 捕獲前面onFulfilled/onRejected拋出的異常 MyPromise.catch = function(onRejected) { return this.then(null, onRejected); } MyPromise.resolve = function (value) { return new Promise(resolve => { resolve(value); }); } MyPromise.race = function(promises) { return new Promise((resolve, reject) => { promises.forEach((promise, index) => { promise.then(resolve, reject); }); }); } 複製代碼