簡單瞭解event loopjavascript
+javascript上, 全部同步任務都在主線程上執行,也能夠理解爲存在一個「執行棧」。主線程不斷的在循環上面的步驟。前端
(function() { console.log('這是開始'); setTimeout(function cb() { console.log('這是來自第一個回調的消息'); setTimeout(function cb3() { console.log("這是來自第三個回調的消息"); }) }); console.log('這是一條消息'); setTimeout(function cb1() { console.log('這是來自第二個回調的消息'); setTimeout(function cb3() { console.log("這是來自第四個回調的消息"); }) }); console.log('這是結束'); })();
什麼是Promisejava
爲何要使用Promisenode
最簡單的Promisenpm
這裏看起來很簡單,但有兩點是要注意的
- 必定要resolve或者reject,不然你的then是永遠也執行不到的。
- promise的狀態必定改變後,就不再會發生變化了。
let promise = new Promise(function(resolve, reject) { resolve("success"); reject("fail"); }); promise.then((value) => { console.log(value); }).catch((reason) => { console.log(reason); });
輸出結果編程
這個例子也充分證實了Promise只有一個狀態結果,而且是不可變的json
經典的回調地獄api
回調函數的寫法promise
多個異步事務多級依賴,回調函數會造成多級的嵌套,代碼就會變成金字塔結構,不只可讀性不高,並且在後期的維護,調試或者重構上,都充滿了風險。併發
doSomething(function(result) { doSomethingElse(result, function(newResult) { doThirdThing(newResult, function(finalResult) { console.log('Got the final result: ' + finalResult); }, failureCallback); }, failureCallback);}, failureCallback);
promise的寫法
解決了嵌套問題,thenable能夠更好的支持鏈式調用,但仍是能看到回調的影子
更加簡便的錯誤處理
doSomething() .then(function(result) { return doSomethingElse(result); }) .then(function(newResult) { return doThirdThing(newResult); }) .then(function(finalResult) { console.log('Got the final result: ' + finalResult); }) .catch(failureCallback);
// 配合箭頭函數
doSomething() .then(result => doSomethingElse(result)) .then(newResult => doThirdThing(newResult)) .then(finalResult => { console.log(`Got the final result: ${finalResult}`); }) .catch(failureCallback);
generator寫法
有一個暫停狀態,只有咱們激活next,纔會去執行下一個異步任務
function* fuc() { const result = yield doSomething() const newResult = yield doSomethingElse(result) const finalResult = yield doThirdThing(newResult) } const test = fuc() const result = test.next() // {value: 1, done: false} const newResult = test.next(result.value) // {value: 1, done: false} const finalResult = test.next(newResult.value) // {value: 1, done: true} test.next() // {value: undefined, done: true}
async/await的寫法
這個語法上更加簡單,看起來更像同步任務,並且不須要關心執行狀態。
我理解這裏只是對generator的一個包裝,裏面應該有個遞歸函數,在執行next,執行done。
async function useAsyncAwait() { try { const result = await doSomething() const newResult = await doSomethingElse() const finalResult = await doThirdThing() console.log('Got the final result: ' + finalResult) } catch (e) { Console.error('exception: ', e) } }
function getJSON(url) { return new Promise(function(resolve, reject){ let xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.onreadystatechange = handler; xhr.responseType = 'json'; xhr.setRequestHeader('Accept', 'application/json'); xhr.send(); function handler() { if (this.readyState === this.DONE) { if (this.status === 200) { resolve(this.response); } else { reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); } } }; }); } getJSON('/posts.json') .then((json) =>{ // on fulfillment }) .catch((error) => { // on rejection console.log(error) });
多個promise順序執行
promise的then,是能夠保證按照你看到的順序執行的
getJSON('/ray/api/a/server/ping') .then((json) =>{ // on fulfillment console.log('ping a result: ' + JSON.stringify(json)); }) .then(() => getJSON('/ray/api/b/server/ping')) .then(json => { console.log('ping b result: ' + JSON.stringify(json)) }) .catch((error) => { // on rejection console.log(error) }); // ping a result: {"status":"OK","serverTime":1573554742633} // ping b result: {"status":"OK","serverTime":1573554742667}
Promise 的鏈式調用
舉一個更具體的例子,體現thenable, promise的狀態不可變
var p = new Promise(function(resolve, reject){ resolve(1); }); p.then(function(value){ //第一個then console.log(value); // 1 return value*2; }).then(function(value){ //第二個then console.log(value); // 2 }).then(function(value){ //第三個then console.log(value); // underfined return Promise.resolve('resolve'); }).then(function(value){ //第四個then console.log(value); // 'resolve' return Promise.reject('reject'); }).then(function(value){ //第五個then console.log('resolve: '+ value); // 不到這裏,沒有值 }, function(err){ console.log('reject: ' + err); // 'reject' })
引用一些第三方庫 好比 Bluebird
好比
mapSeries => 同步的執行全部異步任務,
all => 等待併發的任務所有執行完畢,
any => 多個異步任務中,有一個執行完畢就結束了。
==
npm install bluebird import * as Promise from "bluebird"; let data = ['a', 'c', 'b', 'e', 'd'] Promise.mapSeries(data, (d) => getJSON(d) ).then((result) => {console.log(result)}) // 執行結果應該是什麼 1. 若是沒有修改代碼 => 只執行了第一個a,後面的都應該第一個a出錯,因此不繼續執行了 2. 若是將getJSON裏的reject改爲resoleve => a c b e d的出錯log會按順序打印
Promise.all([ getJSON('/ray/api/a/server/ping'), getJSON('/ray/api/b/server/ping')]).then((result) => { console.log('ping result: ' + JSON.stringify(result)); }) // ping result: [{"status":"OK","serverTime":1573554934072},{"status":"OK","serverTime":1573554934070}]
promise 沒法取消執行
new Promise 裏的任務會當即執行
const delay = new Promise((resolve, reject) => { setTimeout(() => { console.log('timeout'); resolve('timeout'); }, 3000) }) Promise.race([ getJSON('/ray/api/a/server/ping'), delay]).then((result) => { console.log('ping result: ' + JSON.stringify(result)); }) // 思考下這裏的timeout會不會打印
promise 的then必須接收函數,不然會穿透。
// 這裏`Promise.resolve(2)`並非函數,因此上一個函數的結果會穿透到下一個 Promise.resolve(1).then(Promise.resolve(2)).then((v) => { console.log(v) }) // 語法錯誤 Promise.resolve(1).then(return Promise.resolve(2)).then((v) => { console.log(v) }) // 穿透 Promise.resolve(1).then(null).then((v) => { console.log(v) }) // 語法錯誤 Promise.resolve(1).then(return 2).then((v) => { console.log(v) }) // then會返回新的promise,而且帶上他返回的結果 Promise.resolve(1).then(() => { return 2 }).then((v) => { console.log(v) })
當then()受非函數的參數時,會解釋爲then(null),這就致使前一個Promise的結果穿透到下面一個Promise。因此要提醒你本身:永遠給then()傳遞一個函數參數。
錯誤的捕獲
一旦捕獲到錯誤,promise的then會繼續執行
catch會檢查promis鏈上位於它以前的每一個地方(then或者其餘異步操做)。若是在它以前還有其餘catch,那麼起點就是上一個catch。
var p1 = new Promise( function(resolve,reject){ foo.bar(); resolve( 1 ); }); p1.then( function(value){ console.log('p1 then value: ' + value); }, function(err){ console.log('p1 then err: ' + err); } ).then( function(value){ console.log('p1 then then value: '+value); }, function(err){ console.log('p1 then then err: ' + err); } );