出處:https://blog.csdn.net/u010297791/article/details/71158212
(1)上面主要講了同步和回調執行順序的問題,接着我就舉一個包含同步、異步、回調的例子。
let a = new Promise(//聲明了一個Promise回調函數,能夠使用then function(resolve, reject) { console.log(1) setTimeout(() => console.log(2), 0) console.log(3) console.log(4) resolve(true) } ) a.then(v => { console.log(8) }) let b = new Promise( function() { console.log(5) setTimeout(() => console.log(6), 0) } ) console.log(7)
在看正確結果之前,我先進行分析題目(訪問順序:同步 => 異步 => 回調):
1)看同步代碼:a變量是一個Promise,Promise的異步指的是他的then()和catch()方法,Promise本身還是同步的,所以這里先執行a變量內部的Promise同步代碼。(同步優先)
console.log(1)
setTimeout(() => console.log(2), 0) //回調
console.log(3)
console.log(4)
2)Promise內部有4個console,第二個是一個setTimeout回調(回調墊底(意思就是你先讓它等着),所以暫時不輸出)。所以這里先輸出1,3,4,回調的方法丟到消息隊列中排隊等着。
3)接着執行resolve(true),進入then(),then是異步,下面還有同步沒執行完呢,所以then也去消息隊列排隊等候。(異步靠邊)
4)b變量也是一個Promise,和a一樣,同樣是同步的,執行內部的同步代碼,輸出5,setTimeout是回調,去消息隊列排隊等候,這里輸出5。
5)最下面同步輸出7。
6)現在同步的代碼執行完了,JavaScript就跑去消息隊列呼叫異步的代碼:異步,出來執行了。這里只有一個異步then,所以輸出8。
7)此時,異步也over,輪到回調函數:回調,出來執行了。這里有2個回調在排隊,他們的時間都設置為0,所以不受時間影響,只跟排隊先后順序有關。則先輸出a里面的回調2,最后輸出b里面的回調6。
8)最終輸出結果就是:1、3、4、5、7、8、2、6。
(2)分析下面這個例子的執行順序
var Pro = function () { //返回一個Promise對象 return new Promise(function (resolve, reject) { //模擬接口調用 setTimeout(function () {//1,回調等待,同步執行 resolve(true);//4,然后進入then函數 }, 1000); }) }; var Pro2 = function () { //返回一個Promise對象 return new Promise(function (resolve, reject) { //模擬接口調用 setTimeout(function () {//2,回調等待 resolve(‘Pro2成功執行’);//9,訪問另一個then }, 1000); }) }; Pro().then(function(data){//3異步等待 var val = data;//5,resolve傳入的data是true console.log(val)//6,所以先輸出true if (val) { console.log(1111)//7,再輸出1111 return Pro2()//8 } }).then(function(data1){//8異步等待 console.log(data1)//10,輸出Pro2成功執行 })
輸出:
(3)async/await(代替了promise)
可以先看http://www.ruanyifeng.com/blog/2015/05/async.html
async 函數返回一個 Promise 對象,可以使用 then 方法添加回調函數。當函數執行的時候,一旦遇到 await 就會先返回,等到觸發的異步操作完成,再接着執行函數體內后面的語句。
下面是一個例子。
//getStockSymbol(name)和getStockPrice(symbol)都是異步函數,這樣才能使用
//await進行聲明,當兩個異步函數調用完后,返回一個Promise對象,其值為
//stockPrice
async function getStockPriceByName(name) {
var symbol = await getStockSymbol(name);
var stockPrice = await getStockPrice(symbol);
return stockPrice;
}
//然后就可以使用then函數來得到返回的值result == stockPrice
getStockPriceByName('goog').then(function (result){
console.log(result);
});
//如果在函數function (result){}中運行了比較復雜的語句最好是加上catch來捕獲err,如:
getStockPriceByName('goog').then(function (result){
console.log(result);
}).catch((err) =>{
console.log(err);
});
上面代碼是一個獲取股票報價的函數,函數前面的async關鍵字,表明該函數內部有異步操作。調用該函數時,會立即返回一個Promise對象。
這個東西的使用手法就是:
首先你先寫一個return new Promise的回調function,這個function就不用聲明為async,然后就可以在另一個聲明為async的函數中使用這個回調function ,使用時前面表明await,await等待的雖然是promise對象,但不必寫.then(..),直接可以得到返回值;另一個使用的辦法則是直接使用then函數回調,然后用catch函數捕捉錯誤。
在async函數中如果有await的聲明,只能說后面要進行的操作是異步操作來獲得返回值的,如果先后,如:
let c = await count();
let l = await list();
return {count:c,list:l};
只是說明兩者只是寫的時候好像有了個前后關系,但是他們不是同步的,而是異步的,所以他們可以同時進行運算,然后等待兩者結果都出來了以后進行拼裝罷了
當函數執行的時候,一旦遇到 await 就會先返回,等到觸發的異步操作完成,再接着執行函數體內后面的語句
我是這么理解promise和async/await兩者的使用的,我覺得當要使用過多異步的時候,使用async/await更好,比如:
var id; create(user1,’0x0000002’,10,10).then((result) => { if(result){ console.log(result); return owned(user1); //得到user1創建的tokenID } }).then((result) => { if(result){ id = result; console.log('num is :' + result); return sell(result); //賣掉該token } }).then((result) => { if(result){ console.log(result); return buy(user2,40,id); } }).catch((err) =>{ console.log(err); });
當我們想要對一系列回調函數進行有序測試時,如果使用的是then,那么最后看起來真的很繁雜;但是如果使用的是async/await,那么就可以變成:
var test = async() => { try{ await create(user1,’0x0000002’,10,10); var id = await owned(user1); await sell(id); await buy(user2,40,id); }catch(err){ console.log(err); } }
光是看起來就好很多了
注意:當一個函數被聲明為async時,說明它是異步的,說明它的返回值是promise類型的,調用函數后后面可以用then進行返回值的調用,而不是說它的函數中一定要出現await。