參考鏈接:
Promise.then鏈式調(diào)用順序?(這一篇詳細講解then中嵌套promise和各種情況說明河绽,必看,研究)
《ES6標準入門》(阮一峰)--16.Promise 對象?(這一篇講解promise比較全序仙,有promise的各種方法講解)
Promise基本使用及方法介紹?(promise講解比較全谬返,輔助參考)
Promise中then的執(zhí)行順序詳解?(有簡化版then實現(xiàn),參考)
面試官:“你能手寫一個 Promise 嗎”?(手寫then光涂,研究庞萍,未看)
Promise的含義:
Promise 是一個對象,從它可以獲取異步操作的消息忘闻。Promise 是目前前端解決異步問題的統(tǒng)一方案
Promise對象有以下兩個特點:
(1)對象的狀態(tài)不受外界影響钝计。Promise對象代表一個異步操作,有三種狀態(tài):pending(進行中)齐佳、fulfilled(已成功)和rejected(已失斔教瘛)。
只有異步操作的結(jié)果炼吴,可以決定當(dāng)前是哪一種狀態(tài)本鸣,任何其他操作都無法改變這個狀態(tài)。
(2)一旦狀態(tài)改變硅蹦,就不會再變荣德,任何時候都可以得到這個結(jié)果闷煤。Promise 對象的狀態(tài)改變,只有兩種可能:從進行中變?yōu)橐殉晒蛷倪M行中變?yōu)橐咽 ?/p>
只要這兩種情況發(fā)生涮瞻,狀態(tài)就不會再變了鲤拿,會一直保持這個結(jié)果,這時就稱為 resolved(已定型)署咽。如果改變已經(jīng)發(fā)生了近顷,你再對 Promise 對象添加回調(diào)函數(shù),也會立即得到這個結(jié)果艇抠。
promise優(yōu)點:
promise對象幕庐,可以將 異步操作 以 同步操作的流程 表達出來,避免層層嵌套
Promise缺點:
首先家淤,無法取消Promise异剥,一旦新建它就會立即執(zhí)行,無法中途取消絮重。
其次冤寿,如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯誤青伤,不會反應(yīng)到外部督怜。
第三,當(dāng)處于進行中狀態(tài)時狠角,無法得知目前進展到哪一個階段(剛剛開始還是即將完成)号杠。
promise的基本用法:
promise內(nèi)部是同步的,但是then方法是異步的
Promise構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù)丰歌,該函數(shù)的兩個參數(shù)分別是resolve和reject姨蟋。它們是兩個函數(shù),由 JavaScript 引擎提供立帖,不用自己部署眼溶。
resolve 函數(shù)的作用是,將Promise 對象的狀態(tài)從“未完成”變?yōu)椤俺晒Α保磸?pending 變?yōu)?fulfilled)晓勇,在異步操作成功時調(diào)用堂飞,并將異步操作的結(jié)果,作為參數(shù)傳遞出去绑咱;
reject 函數(shù)的作用是绰筛,將Promise 對象的狀態(tài)從“未完成”變?yōu)椤笆 保磸?pending 變?yōu)?rejected),在異步操作失敗時調(diào)用描融,并將異步操作報出的錯誤别智,作為參數(shù)傳遞出去。
Promise實例生成以后稼稿,可以用then方法分別指定fulfilled狀態(tài)和rejected狀態(tài)的回調(diào)函數(shù)薄榛。
Promise.then(function(value) {
? ? // success
}, function(error) {
? ? // failure
})
then方法可以接受兩個回調(diào)函數(shù)作為參數(shù)。第一個回調(diào)函數(shù)是Promise 對象的狀態(tài)變?yōu)閞esolved時調(diào)用让歼,第二個回調(diào)函數(shù)是Promise 對象的狀態(tài)變?yōu)閞ejected時調(diào)用敞恋。
其中,第二個函數(shù)是可選的谋右,不一定要提供硬猫。這兩個函數(shù)都接受Promise 對象傳出的值作為參數(shù)。
下面是一個用Promise對象實現(xiàn)的 Ajax 操作的例子改执。
const getJSON = function(url) {
? const promise = new Promise(function(resolve, reject){
? ? const handler = function() {
? ? ? if (this.readyState !== 4) {
? ? ? ? return;
? ? ? }
? ? ? if (this.status === 200) {
? ? ? ? resolve(this.response);
? ? ? } else {
? ? ? ? reject(new Error(this.statusText));
? ? ? }
? ? };
? ? const client = new XMLHttpRequest();
? ? client.open("GET", url);
? ? client.onreadystatechange = handler;
? ? client.responseType = "json";
? ? client.setRequestHeader("Accept", "application/json");
? ? client.send();
? });
? return promise;
};
getJSON("/posts.json").then(function(json) {
? console.log('Contents: ' + json);
}, function(error) {
? console.error('出錯了', error);
});
resolve函數(shù)的參數(shù)除了正常的值以外啸蜜,還可能是另一個 Promise 實例
const p1 = new Promise(function (resolve, reject) {
? // ...
});
const p2 = new Promise(function (resolve, reject) {
? // ...
? resolve(p1);
})
上面代碼中,p1和p2都是 Promise 的實例辈挂,但是p2的resolve方法將p1作為參數(shù)衬横,即一個異步操作的結(jié)果是返回另一個異步操作。
注意终蒂,這時p1的狀態(tài)就會傳遞給p2蜂林,也就是說,p1的狀態(tài)決定了p2的狀態(tài)拇泣。如果p1的狀態(tài)是pending噪叙,那么p2的回調(diào)函數(shù)就會等待p1的狀態(tài)改變;
如果p1的狀態(tài)已經(jīng)是resolved或者rejected霉翔,那么p2的回調(diào)函數(shù)將會立刻執(zhí)行睁蕾。
Promise.prototype.then():
總結(jié)三點:
(1) 當(dāng)前一個then中的代碼都是同步執(zhí)行的,執(zhí)行結(jié)束后第二個then即可注冊進入微任務(wù)隊列债朵。
(2) 當(dāng)前一個then中有return 關(guān)鍵字子眶,需要return的內(nèi)容完全執(zhí)行結(jié)束,第二個then才會注冊進入微任務(wù)隊列。
(3)then 方法接受的參數(shù)是函數(shù)葱弟,而如果傳遞的并非是一個函數(shù)壹店,它實際上會將其解釋為 then(null),這就會導(dǎo)致前一個 Promise 的結(jié)果會穿透下面芝加。
Promise.resolve(1)
? .then(2)
? .then(Promise.resolve(3))
? .then(console.log)
// 1
Promise 實例具有then方法硅卢,也就是說,then方法是定義在原型對象Promise.prototype上的藏杖。它的作用是為 Promise 實例添加狀態(tài)改變時的回調(diào)函數(shù)将塑。
前面說過,then方法的第一個參數(shù)是resolved狀態(tài)的回調(diào)函數(shù)蝌麸,第二個參數(shù)(可選)是rejected狀態(tài)的回調(diào)函數(shù)点寥。
then方法返回的是一個新的Promise實例(注意,不是原來那個Promise實例)来吩。因此可以采用鏈式寫法敢辩,即then方法后面再調(diào)用另一個then方法蔽莱。
const getJSON = ()=>(new Promise((resolve,reject)=>resolve(1)))
getJSON().then(function(json) {
console.log(json)
return json;
}).then(function(post) {
console.log(post)
});
上面的代碼使用then方法,依次指定了兩個回調(diào)函數(shù)戚长。第一個回調(diào)函數(shù)完成以后盗冷,會將返回結(jié)果作為參數(shù),傳入第二個回調(diào)函數(shù)同廉。
采用鏈式的then仪糖,可以指定一組按照次序調(diào)用的回調(diào)函數(shù)。這時迫肖,前一個回調(diào)函數(shù)锅劝,有可能返回的還是一個Promise對象(即有異步操作),
這時后一個回調(diào)函數(shù)蟆湖,就會等待該Promise對象的狀態(tài)發(fā)生變化故爵,才會被調(diào)用。
const getJSON = (num)=>(new Promise((resolve,reject)=>resolve(num)))
getJSON(1).then(function(post) {
return getJSON(2);
}).then(function (comments) {
console.log("resolved: ", comments);
}, function (err){
console.log("rejected: ", err);
});
// 打诱室觥:resolved:? 2
上面代碼中稠集,第一個then方法指定的回調(diào)函數(shù),返回的是另一個Promise對象饥瓷。這時剥纷,第二個then方法指定的回調(diào)函數(shù),就會等待這個新的Promise對象狀態(tài)發(fā)生變化呢铆。
如果變?yōu)閞esolved晦鞋,就調(diào)用第一個回調(diào)函數(shù),如果狀態(tài)變?yōu)閞ejected棺克,就調(diào)用第二個回調(diào)函數(shù)悠垛。
Promise.resolve():
promise.resolve()可以將對象轉(zhuǎn)換為promise對象
Promise.resolve('foo')
// 等價于
new Promise(resolve => resolve('foo'))
Promise.resolve方法的參數(shù)分成四種情況。
(1)參數(shù)是一個 Promise 實例
如果參數(shù)是 Promise 實例娜谊,那么Promise.resolve將不做任何修改确买、原封不動地返回這個實例。
(2)參數(shù)是一個thenable對象
thenable對象指的是具有then方法的對象纱皆,比如下面這個對象湾趾。
let thenable = {
? then: function(resolve, reject) {
? ? resolve(42);
? }
};
Promise.resolve方法會將這個對象轉(zhuǎn)為 Promise 對象,然后就立即執(zhí)行thenable對象的then方法派草。
let thenable = {
? then: function(resolve, reject) {
? ? resolve(42);
? }
};
let p1 = Promise.resolve(thenable);
p1.then(function(value) {
? console.log(value);? // 42
});
上面代碼中搀缠,thenable對象的then方法執(zhí)行后,對象p1的狀態(tài)就變?yōu)閞esolved近迁,從而立即執(zhí)行最后那個then方法指定的回調(diào)函數(shù)艺普,輸出 42。
(3)參數(shù)不是具有then方法的對象,或根本就不是對象
如果參數(shù)是一個原始值歧譬,或者是一個不具有then方法的對象岸浑,則Promise.resolve方法返回一個新的 Promise 對象,狀態(tài)為resolved瑰步。
const p = Promise.resolve('Hello');
p.then(function (s){
? console.log(s)
});
// Hello
上面代碼生成一個新的 Promise 對象的實例p助琐。由于字符串Hello不屬于異步操作(判斷方法是字符串對象不具有 then 方法),返回 Promise 實例的狀態(tài)從一生成就是resolved面氓,所以回調(diào)函數(shù)會立即執(zhí)行。Promise.resolve方法的參數(shù)蛆橡,會同時傳給回調(diào)函數(shù)舌界。
Promise.prototype.catch():
Promise.prototype.catch() 是 .then(null, rejection) 的別名,用于指定發(fā)生錯誤時的回調(diào)函數(shù)
如果promise實例對象的狀態(tài)變?yōu)閞ejected泰演,
就會觸發(fā) catch() 方法指定的回調(diào)函數(shù)
如果 .then() 方法指定的回調(diào)函數(shù)在運行中拋出錯誤呻拌,也會被 catch() 方法捕獲
promise對象的錯誤具有冒泡性質(zhì),
會一直向后傳遞睦焕,直到被捕獲為止
( 也就是說錯誤總是會被下一個catch語句捕獲 )
一般來說藐握,不要在.then()方法中定義rejected狀態(tài)的回調(diào)函數(shù),
而總是使用 .catch()方法
一般總是建議垃喊,promise對象后要跟 catch()方法猾普,這樣可以處理 promise內(nèi)部發(fā)生法的錯誤,catch() 方法返回的還是promise對象本谜,
因此后面還可以接著調(diào)用 then() 方法
catch() 方法中還能再拋錯誤初家,如果 catch()方法拋出錯誤后,后面沒有catch()方法乌助,錯誤就不會被捕獲溜在,也不會傳遞到外層。
如果catch()方法拋出錯誤后他托,后面有then()方法掖肋,會照常執(zhí)行,后面有catch()方法赏参,錯誤還會被再一次捕獲
async和await:
Async返回的是Promise對象
async/await其實就是promise和generator的語法糖:
async function demo01() {
console.log(1)
return ‘demo1’;
}
demo01().then(function(a){
console.log(a)
});
輸出結(jié)果為1志笼,demo1
async用來表示函數(shù)是異步的,定義的函數(shù)會返回一個promise對象登刺,可以使用then方法添加回調(diào)函數(shù)籽腕。上列代碼中的async函數(shù)中的console.log(1)可以當(dāng)一個同步的隊列執(zhí)行,也就是說但你在定義async function的時候 里面的代碼是以同步的方式執(zhí)行纸俭,只不過async的返回是一個promise皇耗,可以用.then()的方式去執(zhí)行回調(diào),如同上述內(nèi)容揍很。若 async 定義的函數(shù)有返回值郎楼,return ‘demo1’;相當(dāng)于Promise.resolve('demo1’),沒有聲明式的 return則相當(dāng)于執(zhí)行了Promise.resolve();
await是和async一起來使用万伤,一般為:
async function demo2(){
console.log(7)
await a()
console.log(6)
}
demo2()
function a() {
console.log(‘demo2’)
}
當(dāng)他和promise一起用的話:
async myFun( ){ await new Promise(resolve=>{ }) }
總結(jié):
一般async與await同步出現(xiàn)
async返回promise對象
await出現(xiàn)在async函數(shù)內(nèi)部,單獨只用會報錯
componentDidMount() {
? ? ? ? const funSync1 = () => console.log('我是同步函數(shù)1111111')
? ? ? ? const funSync2 = () => console.log('我是同步函數(shù)2222222')
? ? ? ? const funAsync = async () => {? ? // async關(guān)鍵字呜袁,定義的函數(shù)是異步函數(shù)敌买,返回promise對象
? ? ? ? ? ? await funSync1()
? ? ? ? }
? ? ? ? funAsync().then(funSync2())
? ? ? ? console.log('bbbb')
}
// 先把兩個同步函數(shù)變成了異步,在異步函數(shù)中阶界,先執(zhí)行funSync1虹钮,后執(zhí)行funSync2
// 使用async關(guān)鍵字后,會把同步包裝成的異步函數(shù)膘融,按同步方式執(zhí)行
// 所以最后得到的輸出順序是:
// 我是同步函數(shù)1111111
// 我是同步函數(shù)2222222
// bbbb
異步和同步:
同步直接拿到結(jié)果
異步不能直接拿到結(jié)果
回調(diào)不一定用在異步芙粱,也可以用在同步
異步不一定要用到回調(diào),也可以用輪詢氧映。
異步任務(wù)不能拿到結(jié)果
異步任務(wù)完成時調(diào)用回調(diào)
結(jié)果作為參數(shù)
下面是一些例子春畔,也有面試題,主要用來輔助弄懂js執(zhí)行的順序和promise.then執(zhí)行順序:
例子1:
const p1 = () => (new Promise((resolve, reject) => {
????????????????console.log(1);
????????????????let p2 = new Promise((resolve, reject) => {
????????????????????console.log(2);
????????????????????const timeOut1 = setTimeout(() => {
????????????????????????console.log(3);
????????????????????????resolve(4); // 不執(zhí)行代碼岛都,因為p2的promise對象已經(jīng)有結(jié)果
????????????????????}, 0)
????????????????????resolve(5);
????????????????});
????????????????resolve(6);
????????????????p2.then((arg) => {
????????????????????console.log(arg);
????????????????});
????????????}));
????????????const timeOut2 = setTimeout(() => {
????????????????console.log(8);
????????????????const p3 = new Promise(reject => {
????????????????????reject(9);
????????????????}).then(res => {
????????????????????console.log(res)
????????????????})
????????????}, 0)
????????????p1().then((arg) => {
????????????????console.log(arg);
????????????});
????????????console.log(10);
????????????// 打印結(jié)果:1,2,10,5,6,8,9,3
????????????第一輪
????????????宏任務(wù):timeOut2律姨,timeOut1
????????????微任務(wù):p2.then,p1.then(本輪結(jié)束前清空)
????????????打印:1臼疫,2择份,10,5多矮,6
????????????第二輪
????????????宏任務(wù):timeOut2缓淹,timeOut1
????????????微任務(wù):p3.then(本輪結(jié)束前清空)
????????????打印:8塔逃,9
????????????第三輪
????????????宏任務(wù):timeOut1
????????????微任務(wù):
????????????打友逗:3
例子2:
console.log('script start');
????????????// Promise.resolve('foo') 等價于 new Promise(resolve => resolve('foo')),所以會先讓同步任務(wù)先執(zhí)行湾盗,所以這里打印script start伏蚊,然后是script end。
????????????setTimeout(function () {
????????????????console.log('setTimeout---0');
????????????}, 0);
????????????setTimeout(function () {
????????????????console.log('setTimeout---200');
????????????????setTimeout(function () {
????????????????????console.log('inner-setTimeout---0');
????????????????});
????????????????Promise.resolve().then(function () {
????????????????????console.log('promise5');
????????????????});
????????????}, 200);
????????????Promise.resolve().then(function () {
????????????????console.log('promise1');
????????????????// 每個new promise內(nèi)有resolve會先優(yōu)先于.then執(zhí)行格粪,所以這里會先打印promise1躏吊,然后打印promise3,再打印promise2(個人理解帐萎,源碼類似這個邏輯)
????????????}).then(function () {
????????????????console.log('promise2');
????????????});
????????????Promise.resolve().then(function () {
????????????????console.log('promise3');
????????????});
????????????console.log('script end');
????????????// 執(zhí)行結(jié)果: script start矿微,script end五续,promise1,promise3,promise2励负,setTimeout---0,setTimeout---200,promise5,inner-setTimeout---0
例子3:
const promise = new Promise((resolve, reject) => {
????????????????console.log(1)
????????????????????setTimeout(() => {
????????????????????????console.log('once')
????????????????????????resolve('success')
????????????????????}, 1000)
????????????})
????????????// 1打印后舰攒,promise.then剛開始沒有執(zhí)行,因為promise對象還沒有結(jié)果悔醋,也就是狀態(tài)還是pendding摩窃,等到執(zhí)行setTimeout后有了狀態(tài)后才執(zhí)行promise.then
????????????// 另外因為狀態(tài)改變后不會再改變,所以兩個promise.then打印都是一樣結(jié)果
????????????promise.then((res) => {
????????????????console.log(res)
????????????????})
????????????promise.then((res) => {
????????????????console.log(res)
????????????})
????????????// 執(zhí)行結(jié)果:1芬骄,once猾愿,success,success
例子4:
let thenable = {
????????????????then: function(resolve, reject) {
????????????????????resolve(6);
????????????????}
????????????};
????????????// let p = Promise.resolve();
????????????let p = new Promise((resolve, reject) =>{
????????????????resolve(7)
????????????});
????????????setTimeout(()=>{
????????????????console.log(1)
????????????},0)
????????????// Promise.resolve()方法返回一個新的 Promise 對象账阻,狀態(tài)為resolved匪蟀。Promise.resolve().then就是微任務(wù),所有比setTimeout提前打印
????????????// promise.resolve()方法:參數(shù)不是具有then方法的對象宰僧,或根本就不是對象(也就是2,5观挎,4)和參數(shù)是一個promise對象(也就是7)琴儿,他們的優(yōu)先級高于參數(shù)是一個thenable對象
????????????Promise.resolve().then(()=>{
????????????????console.log(2)
????????????})
????????????Promise.resolve(thenable).then((value)=>{
????????????????console.log(value)
????????????})
????????????Promise.resolve(p).then((v)=>{
????????????????console.log(v)
????????????})
????????????Promise.resolve('5').then(()=>{
????????????????console.log('5')
????????????})
????????????Promise.resolve().then(()=>{
????????????????console.log(4)
????????????})
????????????console.log(3)
????????????//執(zhí)行結(jié)果: 3,2嘁捷,7,5造成,4,6雄嚣,1
例子5:
const first = () => (new Promise((resolve, reject) => {
????????????????console.log(3)
????????????????let p = new Promise((resolve, reject) => {
????????????????????console.log(7)
????????????????????setTimeout(() => {
????????????????????????console.log(5)
????????????????????????resolve(6)
????????????????????}, 0)
????????????????????resolve(1)
????????????????})
????????????????resolve(2)
????????????????p.then((arg) => {
????????????????????console.log(arg)
????????????????})
????????????}))
????????????console.log(4)
????????????// 這道題沒什么特殊晒屎,主要指出要注意new Promise有沒有被放在函數(shù)里,如果放在函數(shù)里缓升,是要等到被調(diào)用(first())才會執(zhí)行鼓鲁,而不是new promise就直接執(zhí)行了
????????????first().then((arg) => {
????????????????console.log(arg)
????????????})
????????????// 執(zhí)行結(jié)果: 4,3港谊,7骇吭,1,2歧寺,5
????????????// 第一輪
????????????// 宏任務(wù):setTimeout
????????????// 微任務(wù):p.then,first.then
????????????// 打釉镎:4,3斜筐,7龙致,1,2
????????????// 第二輪
????????????// 宏任務(wù):setTimeout
????????????// 微任務(wù):
????????????// 打忧炅础:5
例子6:
setTimeout(() => {
????????????????console.log("0")
????????????}, 0)
????????????new Promise((resolve,reject)=>{ // promise1
????????????????console.log("1")
????????????????resolve() // 第一個resolve
????????????}).then(()=>{? ? ? ?
????????????????console.log("2")
????????????????new Promise((resolve,reject)=>{ // promise2
????????????????????console.log("3")
????????????????????resolve() // 第二個resolve
????????????????}).then(()=>{? ? ?
????????????????????console.log("4")
????????????????}).then(()=>{? ? ?
????????????????????console.log("5")
????????????????})
????????????}).then(()=>{?
????????????????console.log("6")
????????????})
????????????new Promise((resolve,reject)=>{ // promise3
????????????????console.log("7")
????????????????resolve() // 第三個resolve
????????????}).then(()=>{? ? ? ?
????????????????console.log("8")
????????????})
????????????// 首先執(zhí)行兩個new Promise打印1目代,7,然后是resolve1和resolve3進入隊列。執(zhí)行resolve1像啼,打印2俘闯,3,resolve2進入隊列(這個時候?qū)τ趐romise1的第一個then來說忽冻,已經(jīng)有了fullfilled狀態(tài)真朗,所以promie1第二個then也進入隊列),
????????????// 所以打印8僧诚,4遮婶, 6,5湖笨,最后打印0
????????????// 執(zhí)行結(jié)果:1,7,2,3,8,4,? 6,5,(這一步要注意旗扑,promise1.then是先進入任務(wù)隊列,不是5慈省,6),0
例子7:
async function async1() {
????????????console.log('async1 start')
????????????await async2()? // 相當(dāng)于async2().then(() => {})臀防,所以下面這個'async1 end'是先被放到微任務(wù)隊列
????????????console.log('async1 end')
????????}
????????async function async2() {
????????????console.log('async2')
????????}
????????console.log('script start')
????????setTimeout(() => {
????????????console.log('setTimeout')
????????}, 0)
????????async1()
????????new Promise(function (resolve) {
????????????console.log('promise1')
????????????resolve()
????????}).then(function () {
????????????console.log('promise2')
????????})
????????console.log('script end')
????????// 這里比較容易弄錯的是async2,async1 end的位置边败。async1 start之后為什么會馬上執(zhí)行async2袱衷,應(yīng)該是這個await的作用(變異步為同步,必須等待await后的函數(shù)先執(zhí)行)
????????// 然后就是await async2()相當(dāng)于async2().then(() => {})笑窜,所以下面這個'async1 end'是先被放到微任務(wù)隊列致燥,Promise.then后面放入微任務(wù)隊列,所以async1 end比promise2先執(zhí)行排截。
????????// 答案依次:script start ==> async1 start ==> async2 ==> promise1 ==>
????????// script end ==> async1 end ==> promise2 ==> setTimeOut
例子8:
// async用來表示函數(shù)是異步的嫌蚤,定義的函數(shù)會返回一個promise對象,可以使用then方法添加回調(diào)函數(shù)断傲。
????????// 也就是說你在定義async function的時候 里面的代碼是以同步的方式執(zhí)行脱吱,只不過async的返回是一個promise,可以用.then()的方式去執(zhí)行回认罩。
????????// 若 async 定義的函數(shù)有返回值急凰,return ‘demo1’;相當(dāng)于Promise.resolve('demo1’),沒有聲明式的 return則相當(dāng)于執(zhí)行了Promise.resolve();
????????async function demo01() {
????????????console.log(1)
????????????return 'demo1';
????????}
????????console.log('0')
????????demo01().then(function(a){
????????????console.log(a)
????????});
????????console.log('8')
????????async function demo2(){
????????????console.log(7)
????????????await a()
????????????console.log(6)
????????}
????????demo2()
????????let promise = new Promise(function(resolve, reject){
????????????resolve(5)
????????????console.log(3)
????????????setTimeout(function(){console.log(4)})
????????})
????????console.log(2)
????????promise.then(function(a){
????????????console.log(a)
????????})
????????function a(){
????????????console.log('demo2')
????????}
????????// 0,1猜年,8抡锈,7,demo2,3,2,demo1,6,5,4
例子9:有點難
const p1 = Promise.resolve();
????????????let p3;
????????????const p2 = new Promise(function(resolve, reject){
????????????// 依賴p1的狀態(tài),比p1延遲了兩個tick即ticke-3執(zhí)行then
????????????p3 = new Promise(res => res(p1));
????????????// 等價于p1.then(tick1).then(tick2).then(logp3)
????????????p3.then(() => {
????????????????// tick-3
????????????????console.log('p3')
????????????????resolve('ok');
????????????})
????????????});
????????????p1.then(() => {
????????????// tick-1
????????????console.log('p1-1')
????????????}).then(() => {
????????????// tick-2
????????????console.log('p1-2')
????????????}).then(() => {
????????????// tick-3
????????????console.log('p1-3')
????????????})
????????????p2.then(function(data) {
????????????// p2在tick-3時resolve則本函數(shù)在tick-4執(zhí)行
????????????console.log('p2-1')
????????????}).then(function(data) {
????????????// tick-5
????????????console.log('p2-2')
????????????}).then(function(data) {
????????????// tick-6
????????????console.log('p2-3')
????????????})
????????????p3.then(function(data) {
????????????// 與p2內(nèi)部的then在同一時刻即tick-3
????????????console.log('p3-1')
????????????}).then(function(data) {
????????????// tick-4
????????????console.log('p3-2')
????????????}).then(function(data) {
????????????// tick-5
????????????console.log('p3-3')
????????????})
根據(jù)不同的tick整理順序可得:
// tick-1: p1-1
????????????// tick-2: p1-2
????????????// tick-3: p3,p3-1,p1-3 【由于p3先入棧tick3任務(wù)故在p1-3之前】
????????????// tick-4: p2-1,p3-2
????????????// tick-5: p2-2,p3-3
????????????// tick-5: p2-3
如果把p3部分的代碼轉(zhuǎn)換下是等效于下面這段的
const p2 = new Promise(function(resolve, reject){
????????????// 依賴p1的狀態(tài),比p1延遲了兩個tick即ticke-3執(zhí)行then
????????????const noop = () => void 0;
????????????p3 = p1.then(noop).then(noop);
????????????// 等價于p1.then(tick1).then(tick2).then(logp3)
????????????p3.then(() => {
????????????????// tick-3
????????????????console.log('p3')
????????????????resolve('ok');
????????????})
????????????});
例子10:
// 下面是兩個例子
// 例子1:
const p1 = new Promise(function (resolve, reject) {
????????????setTimeout(() => reject(new Error('fail')), 3000)
????????})
????????const p2 = new Promise(function (resolve, reject) {
????????????setTimeout(() => {
????????????????console.log('p2')
????????????????resolve(p1)
????????????}, 1000)
????????})
????????p2
????????.then(result => console.log(result))
????????.catch(error => console.log(error))
????????// 打印結(jié)果:p2,Error:fail
????????// 上面代碼中乔外,p1是一個 Promise床三,3 秒之后變?yōu)閞ejected。p2的狀態(tài)在 1 秒之后改變杨幼,resolve方法返回的是p1撇簿。由于p2返回的是另一個 Promise聂渊,
????????// 導(dǎo)致p2自己的狀態(tài)無效了,由p1的狀態(tài)決定p2的狀態(tài)四瘫。所以汉嗽,后面的then語句都變成針對后者(p1)。又過了 2 秒找蜜,p1變?yōu)閞ejected饼暑,導(dǎo)致觸發(fā)catch方法指定的回調(diào)函數(shù)。
????????// 注意洗做,調(diào)用resolve或reject并不會終結(jié) Promise 的參數(shù)函數(shù)的執(zhí)行弓叛。
new Promise((resolve, reject) => {?// 例子2:
????????????resolve(1);
????????????console.log(2);
????????}).then(r => {
????????????console.log(r);
????????});
????????// 2
????????// 1
????????// 上面代碼中,調(diào)用resolve(1)以后诚纸,后面的console.log(2)還是會執(zhí)行撰筷,并且會首先打印出來。這是因為立即 resolved 的 Promise 是在本輪事件循環(huán)的末尾執(zhí)行畦徘,
????????// 總是晚于本輪循環(huán)的同步任務(wù)毕籽。
????????// 一般來說,調(diào)用resolve或reject以后井辆,Promise 的使命就完成了影钉,后繼操作應(yīng)該放到then方法里面,而不應(yīng)該直接寫在resolve或reject的后面掘剪。
????????// 所以,最好在它們前面加上return語句奈虾,這樣就不會有意外夺谁。
????????new Promise((resolve, reject) => {
????????????return resolve(1);
????????????// 后面的語句不會執(zhí)行
????????????console.log(2);
????????})
例子11:
const getJSON = (num)=>(new Promise((resolve,reject)=>resolve(num)))
????????getJSON(1).then(function(json) {
????????????console.log(json)
????????????return json+1;
????????}).then(function(post) {
????????????console.log(post)
????????});
????????// 打印:1肉微,2
????????const getJSON = (num)=>(new Promise((resolve,reject)=>resolve(num)))
????????getJSON(1).then(function(post) {
????????????return getJSON(2);
????????}).then(function (comments) {
????????????console.log("resolved: ", comments);
????????}, function (err){
????????????console.log("rejected: ", err);
????????});
????????// 打迂遗浮:resolved:? 2
例子12:重要
promise.then內(nèi)包含prmise.then,但是內(nèi)部這個promise前面沒有return的情況
new Promise((resolve, reject) => {
????????????console.log("promise")
????????????resolve()
????????})
????????.then(() => {???// 執(zhí)行.then的時候生成一個promise是給最后一個.then的
????????????console.log("then1")
????????????new Promise((resolve, reject) => {
????????????????console.log("then1promise")
????????????????resolve()
????????????})
????????????.then(() => {// 執(zhí)行這個.then的時候碉纳,生成的promise是下面一個then的
????????????????console.log("then1then1")
????????????????setTimeout(()=>{
????????????????????console.log('setTimeout')
????????????????},0)
????????????})
????????????.then(() => {
????????????????console.log("then1then2")
????????????})
????????})
????????.then((res) => {
????????// 這個
????????????console.log("then2",res)
????????})
????????// 執(zhí)行結(jié)果:promise勿负,then1,then1promise劳曹,then1then1奴愉,then2 undefined,then1then2,setTimeout
例子13:重要
promise.then內(nèi)包含prmise.then铁孵,但是內(nèi)部這個promise前面有return的情況
new Promise((resolve, reject) => {
????????????console.log("promise")
????????????resolve()
????????})
????????.then(() => {???// 執(zhí)行.then的時候生成一個promise是給最后一個.then的
????????????console.log("then1")
????????????return new Promise((resolve, reject) => {
????????????????console.log("then1promise")
????????????????resolve()
????????????})
????????????.then(() => {// 執(zhí)行這個.then的時候锭硼,生成的promise是下面一個then的
????????????????console.log("then1then1")
????????????????setTimeout(()=>{
????????????????????console.log('setTimeout')
????????????????},0)
????????????})
????????????.then(() => {
????????????????console.log("then1then2")
????????????})
????????})
????????.then((res) => {
????????// 這個
????????????console.log("then2",res)
????????})
????????// 注意:.then1函數(shù)體有了return返回值,是一個Promise對象蜕劝,而且是then1then2執(zhí)行完返回的匿名Promise對象檀头。所以只有等這個Promise對象resolve了之后轰异,才會執(zhí)行then2回調(diào)函數(shù)體的邏輯,所以’then2’最后才會打印暑始。
????????// 執(zhí)行結(jié)果:promise搭独,then1,then1promise廊镜,then1then1牙肝,then1then2,then2 undefined,setTimeout
關(guān)于例子12和例子13期升,說說自己的一點觀點:內(nèi)部promise在resolve后立即執(zhí)行后面的then(其他不是包含promise也是這樣)惊奇,然后會跳出來執(zhí)行外部promise的一個then,而不是繼續(xù)執(zhí)行內(nèi)部promise的下一個then播赁。如果有return颂郎,情況就不一樣了。
例子14:難容为,不懂乓序,待解釋
1. 當(dāng)Promise對象作為resolve的參數(shù)時
const p = Promise.resolve();
????????const p1 = Promise.resolve(p); //就是p
????????const p2 = new Promise(res => res(p)); //新建一個對象,對象狀態(tài)依賴p
????????// res(p)可以看作 await p1; await resolve();
????????// 或者p.then(data => getData()).then(() => p2.resolve())
????????// 首先坎背;p1 === p; p2!===p
????????// 那么替劈,p1是一個fulfilled狀態(tài)的對象;p2狀態(tài)需要運行后求得
????????console.log(p === p1); // true
????????console.log(p === p2); // false
????????p1.then(() => {
????????????console.log('p1-1');
????????}).then(() => {
????????????console.log('p1-2');
????????}).then(() => {
????????????console.log('p1-3');
????????})
????????p2.then(() => { //p-2.resolve之后才能調(diào)用回調(diào)函數(shù)
????????????console.log('p2-1');
????????}).then(() => {
????????????console.log('p2-2');
????????}).then(() => {
????????????console.log('p2-3');
????????})
????????p.then(() => {
????????????console.log('p-1');
????????}).then(() => {
????????????console.log('p-2');
????????}).then(() => {
????????????console.log('p-3');
????????})
????????//? 運行結(jié)果
????????// getData()
????????// p1-1
????????// p-1
????????// 前面兩個打印因為本身狀態(tài)已經(jīng)resolve了得滤,所以直接出結(jié)果
????????// resolve()
????????// p1-2
????????// p-2
????????// p2-1
????????// p1-3
????????// p-3
????????// p2-2
????????// p2-3
????????// 后面打印p2-1是等到p運行resolve后才開始打印
例子15:難
2. 當(dāng)Promise的resolve方法在另一個Promise對象的then方法中運行時陨献,變異步眨业;
例子12和例子13類似:p1要等resolve才能執(zhí)行后面的then,所以p3先執(zhí)行了龄捡。只要有resolve慷暂,就會先執(zhí)行屬于那個resolve的then聘殖,然后執(zhí)行另外一個promise的resolve的then,最后才會繼續(xù)第一個resolve接下去的then
let p3;
????????p1 = new Promise(resolve => {
????????????p3 = new Promise(res => res());
????????????p3.then(() => {
????????????????console.log('p3')
????????????????resolve(); // resolve()方法用在then方法中行瑞,變?yōu)楫惒綀?zhí)行
????????????})
????????})
????????p1.then(() => {
????????????console.log('p1-1');
????????}).then(() => {
????????????console.log('p1-2');
????????})
????????p3.then(() => {
????????????console.log('p3-1')
????????}).then(() => {
????????????console.log('p3-2')
????????})
????????// 執(zhí)行結(jié)果:p3,p3-1,p1-1,p3-2,p1-2
例子16:
new Promise((r,rj) => {
????????????console.log('外p');
????????????r();
????????}).then(() => {
????????????console.log('外then1');
????????????new Promise(((r,rj) => {
????????????????console.log('內(nèi)p');
????????????????r();
????????????})).then(() => {
????????????????console.log('內(nèi)then1');
????????????????return new Promise((r, rj) => {r();});
????????????}).then(() => {
????????????????console.log('內(nèi)then2');
????????????});
????????}).then(() => {
????????????console.log('外then2');
????????}).then(() => {
????????????console.log('外then3');
????????}).then(() => {
????????????console.log('外then4');
????????});
// ?????return new Promise((r, rj) => {r();})? 等同于
// ? ? ?return new Promise((r, rj) => {r();}).then(()=>{console.log(1)}).then(()=>{console.log(2)}).then(()=>{console.log(3)});
????????// 執(zhí)行結(jié)果:外p奸腺,外then1,內(nèi)p血久,內(nèi)then1洋机, 外then2,外then3洋魂,外then4绷旗,內(nèi)then2喜鼓,
例子17:------
// 例子1:
// await 不過是generator和promise結(jié)合后的一種語法糖????????// await 會生成一個 Promise ,以其后表達式的值 resolve 這個 Promise(類似 Promise.resolve())衔肢,并在它的 then 回調(diào)里執(zhí)行 await 返回之后的一切庄岖,同時結(jié)束程序的執(zhí)行。
????????function doA1(){?
????????????new Promise( function (resolve) {?
????????????????console.log(11);?
????????????????resolve();?
????????????}).then(o=>{?
????????????console.log(12);?
????????????}).then(o=>{?
????????????console.log(13);?
????????????}).then(o=>{?
????????????console.log(14);?
????????????})?
????????}?
????????await doA1();?
????????console.log(2);
????????//執(zhí)行結(jié)果順序為 11 12 2 13 14
????????// 按代碼執(zhí)行順序角骤,首先初始事件循環(huán):
????????// 1.Promise 的 executor 參數(shù)是立即執(zhí)行的隅忿,所以馬上打印 11。
????????// 2.因為馬上 resolve 了邦尊,后面跟著 then 故 promise 進入微事件隊列中背桐,繼續(xù)執(zhí)行其它平臺代碼。
????????// 3.await 因為跟了一個非 Promise 的值所以會自動包上一層 Promise.resolve蝉揍,即無論如何都會至少等一個微事件周期链峭。所以得到的 undefined 也進入微事件隊列。
????????// await 阻塞了又沾,本事件循環(huán)準備結(jié)束,開始清微事件隊列
????????// 1.先是前面 11 得到的 promise杖刷,處理 then 回調(diào)打印 12滑燃,后面的 promise 繼續(xù)入微事件隊列表窘。
????????// 2.接著是 await 得到 undefined蚊丐,因為沒有賦值麦备,被丟棄凛篙。
????????// 3.await 得到了值呛梆,不再阻塞填物,繼續(xù)往下執(zhí)行打印 2。
????????// 4.await 后的平臺代碼執(zhí)行完畢升薯,微事件隊列中還有 12 得到 promise涎劈。
????????// 5.重復(fù)上邊過程打印 13 和 14
????????// 6.微事件隊列已清空阅茶,本事件循環(huán)結(jié)束脸哀。
// 例子2:
????????// 返回了 Promise 那么 await 就會等這個 Promise 都 resolve 了才會繼續(xù)往下執(zhí)行企蹭。
????????function doA1(){
????????????return new Promise( function (resolve) {
????????????????console.log(11);
????????????????resolve();
????????????}).then(o=>{
????????????console.log(12);
????????????}).then(o=>{
????????????console.log(13);
????????????}).then(o=>{
????????????console.log(14);
????????????})
????????}
????????await doA1();
????????console.log(2);
????????//執(zhí)行結(jié)果順序為 11 12 13 14 2
例子18:難
new Promise((r,rj) => {
????????????console.log('外p');
????????????r();
????????}).then(() => {
????????????console.log('外then1');
????????????new Promise(((r,rj) => {
????????????????console.log('內(nèi)p');
????????????????r();
????????????})).then(() => {
????????????????console.log('內(nèi)then1');
????????????????return new Promise((r, rj) => {r();});
????????????}).then(() => {
????????????????console.log('內(nèi)then2');
????????????});
????????}).then(() => {
????????????console.log('外then2');
????????}).then(() => {
????????????console.log('外then3');
????????}).then(() => {
????????????console.log('外then4');
????????});
// ?????return new Promise((r, rj) => {r();})? 等同于
// ? ? ?return new Promise((r, rj) => {r();}).then(()=>{console.log(1)}).then(()=>{console.log(2)}).then(()=>{console.log(3)});
????????// 執(zhí)行結(jié)果:外p谅摄,外then1送漠,內(nèi)p闽寡,內(nèi)then1爷狈, 外then2,外then3思币,外then4谷饿,內(nèi)then2博投,
例子19:
const p1 = new Promise( (resolve,reject) => {
????????????setTimeout(() => {
????????????????reject(new Error('fail'))
????????????????console.log('3s')? // console.log語句仍然會執(zhí)行毅哗,并且在reject()異步函數(shù) 前執(zhí)行
????????????},3000)
????????})
????????const p2 = new Promise( (resolve,reject) => {
????????????setTimeout( () => {
????????????????return resolve(p1)? // 一般都在這里加return,這樣后面的代碼就不會執(zhí)行叉跛,防止意外?昀濉酥艳!
????????????????console.log('1s')
????????????}, 1000 )
????????})
????????p2.then(res => console.log(res))? ? // 并沒有執(zhí)行
????????.catch(error => console.log(error))
????????// 打印結(jié)果:3s充石,Error:fail
????????// 注意: p2.then(res => console.log(....))并沒有執(zhí)行霞玄,因為p2的狀態(tài)變成了p1的狀態(tài),是rejected
????????// p2.then(res => console.log(res,'fulfilled'), res => console.log(res,'rejected'))
????????// 實際執(zhí)行的是上面的 第二個回調(diào)函數(shù)