揭開go的 gorouter,c#的 async/await等 使用同步的寫法寫異步代碼的神祕面紗 , 證實其本質就是一個語法糖javascript
由於js能夠經過編程語言本身的語法特性,實現async/await語法java
const promise = new Promise(function(resolve, reject) { xxxxx.異步IO操做((res)=>{ if(res成功){ resolve(res) }else{ reject(res) } }) });
promise出入的回調函數有必定的要求ajax
Promise實例生成之後,能夠用then方法分別指定resolved狀態和rejected狀態的回調函數(處理返回的結果)。
promise.then(function(value) { // success }, function(error) { // failure });
引伸-注意: promise對象在js中很是特殊,好比下面的例子
const p1 = new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('fail')), 3000) }) const p2 = new Promise(function (resolve, reject) { setTimeout(() => resolve(p1), 1000) }) p2 .then(result => console.log(result)) .catch(error => console.log(error))
這個的結果是failt 由於 p2中resolve返回一個promise對象,這個操做將會致使p2的狀態升級成p1的狀態(標準)
promise then方法將會返回一個promise,因此js支持鏈式異步編程
var getJSON = function (url, callback) { var promise = new Promise(function (resolve, reject) { var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler;//readyState屬性的值由一個值變爲另外一個值時,都會觸發readystatechange事件 client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.readyState !== 4) { return; } if (this.status === 200) { callback(this.response); resolve(this.response); } else { reject(new Error(this.statusText)) } }; }); return promise; }; getJSON("./e2e-tests/get.json", function (resp) { console.log("get:" + resp.name); }).then(function (json) { getJSON("./e2e-tests/get2.json", function (resp) { console.log("get2:" + resp.name); }) }).catch(function (error) { console.log("error1:" + error); });
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));
這個異常捕獲和java相同,捕獲在eventLoop中產生的異常json
注意一點這個異常和java的try catch是不一樣的,若是產生了異常將不會在主線程中顯示出來
這個和java的異常體系相同,finally 無關狀態,最後都會執行c#
Promise.resolve(2).finally(() => {})
Promise.resolve('foo') // 等價於 new Promise(resolve => resolve('foo'))
注意: promise異步化結果只能在回調函數中得到,若是異步的操做太多,將會調至調用鏈路過長
promise 寫法有什麼問題? ---- 調用鏈路過長promise
好比: 使用promise 實現 異步ajax請求app
var getJSON = function (url, callback) { var promise = new Promise(function (resolve, reject) { var client = new XMLHttpRequest(); client.open("GET", url); client.onreadystatechange = handler;//readyState屬性的值由一個值變爲另外一個值時,都會觸發readystatechange事件 client.responseType = "json"; client.setRequestHeader("Accept", "application/json"); client.send(); function handler() { if (this.readyState !== 4) { return; } if (this.status === 200) { callback(this.response); resolve(this.response); } else { reject(new Error(this.statusText)) } }; }); return promise; }; getJSON("./e2e-tests/get.json", function (resp) { console.log("get:" + resp.name); }).then(function (json) { getJSON("./e2e-tests/get2.json", function (resp) { console.log("get2:" + resp.name); }) }).catch(function (error) { console.log("error1:" + error); });
調用鏈太長,不停的promise調用dom
generator:js的特殊語法,使用yield 關鍵字將函數分塊了,而後可使用遍歷器手動控制執行
例子:異步
function * gen(){ let a= 123; let b = yield a; let c = yield a+b; return a+b+c; } let start = gen(); console.log(start.next()); console.log(start.next(2)); console.log(start.next(3));
本質上是函數分片
js在每次yield的時候都會得到當前位置的表達式,而後再手動的嵌入就能夠實現分片控制的效果了
看一下這個方法
function* asyncFn(value) { let a = yield promiseOne(value); let b = yield promiseTwo(a); return a + b; }
想讓他能異步執行,只要能讓前一個promise的結果是下一個promise的輸入就能夠了
這裏有兩種寫法
遞歸方程: f(最終結果) = f(到目前的結果)+f(接下來執行的結果)
function promiseOne(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) } function promiseTwo(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) } function* asyncFn(value) { let a = yield promiseOne(value); let b = yield promiseTwo(a); return a + b; } function runAsync(fn,value) { let item = fn.next(value); return new Promise((res, rej) => { if (!item.done) { if (item.value instanceof Promise) { item.value.then((re)=>{ runAsync(fn,re).then(res); }) } else { runAsync(fn,fn.valueOf()).then(res); } } else { res(item.value);//這個res方法實際上是全部人的res方法 } }) } runAsync(asyncFn(12)).then(res=>{ console.log(res); });
co 工具包的寫法
function run (gen) { gen = gen() return next(gen.next()) function next ({done, value}) { return new Promise(resolve => { if (done) { // finish resolve(value) } else { // not yet value.then(data => { next(gen.next(data)).then(resolve) }) } }) } } function getRandom () { return new Promise(resolve => { setTimeout(_ => resolve(Math.random() * 10 | 0), 1000) }) } function * main () { let num1 = yield getRandom() let num2 = yield getRandom() return num1 + num2 } run(main).then(data => { console.log(`got data: ${data}`); })
遞歸方程 f(最終結果) = f(以前全部的結果)+f(最後一步的結果)
//同步方式寫異步 function asyncRun(resf, fn, value) { let a = fn(value); go(value); function go(value) { let next = a.next(value); if (!next.done) { if (next.value instanceof Promise) { next.value.then((res) => { go(res); }); } else { return go(next.value); } } else { resf(next.value); } } } function* asyncFn(value) { let a = yield promiseOne(value); let b = yield promiseTwo(a); return a + b; } function show(item) { console.log(item) } asyncRun(show, asyncFn, 12); function promiseOne(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) } function promiseTwo(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) }
上面複雜的代碼若是變成async/await要怎麼作呢
很簡單
// function* asyncFn(value) { // let a = yield promiseOne(value); // let b = yield promiseTwo(a); // return a + b; // } function promiseOne(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) } function promiseTwo(xxx) { return new Promise((res, rej) => { res(xxx + 1); }) } async function asyncFn(value) { let a = await promiseOne(value); let b = await promiseTwo(a); return a + b; } asyncFn(12).then((res)=>{ console.log(res) });
經過上面的例子,咱們能夠發現其實async/await本質上實際上是 generator的一個語法糖
await就是yield , async 的做用就是將函數編程語法糖
若是背的話很簡答兩條規則:
經過這種方法就能夠簡單的實現異步了