(1) var
- var在函數外定義,是全局變量,函數內可以使用
- var在函數內定義,是局部變量,函數外不能使用
- var 在其他代碼塊中定義,是全局變量 ----- 如 if 等
var x = 10;
(
function fun() {
var y = 20;
console.log(x) // 10
}
)();
console.log(y) // 報錯 y is not defined ----- var在函數內部定義,是局部變量,函數外無法讀取
-------------------------------------
if (true) {
var z = 300;
}
console.log(z) // 300 // var在其他代碼塊中定義,是全局變量
(2) 對象es6
- 表達式 - 可以作爲對象的屬性名和方法名
- 變量名可以直接作爲對象屬性名
- 對象中的方法可以簡寫
let ani = 'animal'
let nam = 'name'
let newAr = {
ani // 變量名直接作爲對象的屬性名,等同於 ( ani: ani 即 ani: animal) 前者是字符串,後者ani是變量
nam,
[ani + nam] : 2000, // 中括號中寫表達式,作爲對象的屬性名或方法名
getAge() { // 方法名簡寫,相當於 getAge: funciton() { console.log(this.animalname) }
console.log(this.animalname)
}
}
newAr.getAge();
console.log(newAr,'newAr')
Promise
Promise含義
- promise是一種異步編程的解決方案
- 比傳統的 回調函數和事件 更強大
Promise具體是什麼?
- promise是一個容器,裏面保存着某個未來纔會結束的事件 (比如 異步操作的結果)
- 語法上:promise是一個對象,可以從它獲取異步操作的消息
- promise提供統一的api,所以各種異步操作都可以用同樣的方法進行處理
Promise對象的特點?
- promise對象的狀態不受外界影響 ----- ( promise有三種狀態 )
pending進行中,fulfilled已成功,rejected已失敗
只有異步操作的結果可以決定當前是哪一種狀態,其他別的操作無法改變這個狀態 - promise的狀態一旦改變,就不會再變,任何時候都能得到這個結果
promise狀態改變只有兩種情況:pending狀態 變爲 fulfilled狀態
和pending狀態 變爲 rejected狀態
只要這兩種情況發生,狀態就凝固了,不會再改變,稱爲 ( resolved ) 已定型
例如:只要改變已經發生了,你再對promise對象添加回調函數,也會立即得到這個結果
Promise vs 事件?
Promise:只要改變已經發生了,你再對promise對象添加回調函數,也會立即得到這個結果
event----: 如果改變發生了,你再去監聽,得不到結果
Promise對象 的優缺點?
- 優點
promise對象,可以將 異步操作 以 同步操作的流程 表達出來,避免層層嵌套 - 缺點
(1) promise一旦新建,就會立即執行,無法取消
(2) 如果不設置回掉函數,promise內部拋出的錯誤就不會反應到外部
(3) 處於pending狀態時,是不能知道目前進展到哪個階段的 ( 剛開始?,即將結束?)
Promise構造函數
es6規定,Promise對象 是一個構造函數,用來生成Promise實例
- Promise構造函數,接受一個函數作爲參數,該函數有兩個參數分別是 resolve 和
reject,它們是兩個函數 - resolve 和 reject 由 javascript 引擎提供,不用自己部署
- resolve函數
resolve函數的作用是,將Promise對象的狀態,由pending=>變爲fulfilled,在異步操作成功時調用,並將異步操作的結果,作爲參數傳遞出去 - reject函數
reject函數的作用是,將Promise對象的狀態,由pending=>變爲rejected,在異步操作失敗時調用,並將異步操作報出的錯誤,作爲參數傳遞出去
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 異步操作成功 */){
... 異步操作
resolve(value); // 異步操作的結果作爲參數,傳遞出去
} else {
reject(error);
}
});
- .then 方法
(1) promise實例生成以後,可以用.then方法,分別指定resolved狀態 和 rejected狀態的回調函數
(2) .then方法可以接受 ( 兩個回調函數 ) 作爲參數,
===> 第一個回調函數,在promise對象的狀態變爲 resolved時調用 ------ (該回調函數在promise構造函數中定義,在實例化後,.then方法中調用 )
===> 第二個回調函數,在promise對象的狀態變爲 rejected 時調用 ------ (該回調函數在promise構造函數中定義,在實例化後,.then方法中調用 )
(3) 第二個回調是可選的,可以不提供
(4) 這兩個函數,都接受 promise對象 傳出的值 作爲參數
promise.then(function(value) {
// success
// 該回調函數在promise對象狀態變爲resolved時調用,參數是promise對象 resolved時,傳出的值
}, function(error) {
// failure
});
很重要的一個例子
//很重要的一個例子
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms, 'done');
});
}
timeout(100).then((value) => {
console.log(value);
});
上面代碼中,timeout方法返回一個Promise實例,表示一段時間以後纔會發生的結果。
過了指定的時間(ms參數)以後,Promise實例的狀態變爲resolved,就會觸發then方法綁定的回調函數。
promise新建後會立即執行
// promise新建後會立即執行
let promise = new Promise(function(resolve, reject) {
console.log('Promise');
resolve(); // 這裏沒有傳參,作用是去觸發.then中的回調函數
// resovle()函數的作用,是把promise實例對象的狀態變爲resolved, --------
// 在異步操作成功時調用,並將異步操作的結果作爲參數傳遞出去
// 而當promise實例對象的狀態變爲 resolved時,就會觸發 .then函數中的 回調函數 --------
});
promise.then(function() {
console.log('resolved.');
});
console.log('Hi!');
// Promise // 注意執行順序
// Hi!
// resolved
上面代碼中,Promise 新建後立即執行,所以首先輸出的是Promise。
然後,then方法指定的回調函數,將在當前腳本所有同步任務執行完纔會執行,所以resolved最後輸出。
( 請求圖片 ) https://blog.csdn.net/h1534589653/article/details/77528367
( Image對象 ) https://blog.csdn.net/baihuaxiu123/article/details/53091105
異步加載圖片
componentDidMount() {
function loadImage(url) {
return new Promise((resolve, reject) => {
const image = new Image('400', '200') // 生成image實例對象,寬高
image.onload = () => {
resolve(image) // 加載成功時候,返回image對象
}
image.onerror = () => {
reject( new Error(`could not load image at ${url}`)) // 加載失敗是報錯
}
image.src = url // 請求的圖片地址
})
}
loadImage('http://pic.7y7.com/201410/2014102458431393_600x0.jpg')
.then(res => {
console.log( res.src ) // 拿到image對象的src 屬性
this.setState({
images: res
},() => {console.log(this.state.images)})
}, () => {
// rejected狀態下的回掉函數
})
}
resolve函數,reject函數 ------------- (重要)
- 如果調用resolve和reject函數時,帶有參數,那麼他們的參數會傳給回調函數
- reject函數 的 參數, 通常是 Error對象的實例,表示拋出的錯誤
- resolve函數的參數,除了正常值以外,還可能是另一個promise實例
const p1 = new Promise(function (resolve, reject) {
// ...
});
const p2 = new Promise(function (resolve, reject) {
// ...
resolve(p1); // p2異步操作的結果返回 --> p1 異步操作
})
上面代碼中,p1和p2都是 Promise 的實例,
但是p2的resolve方法將p1作爲參數,即一個異步操作的結果是返回另一個異步操作。
----------------------------------------------------
注意: 上面例子中
p1的狀態決定了p2的狀態
(1) 如果 p1 的狀態是pending,那麼 p2 的回調函數就會等待 p1 的狀態改變
(2) 如果 p1 的狀態是 fulfilled或者rejected,那麼 p2 的回調函數將會立即執行
Promise嵌套 ---------------------------(重要)(重要)(重要)
componentDidMount() {
let i = 0
setInterval(() => {
console.log(`經過了${++i}s`)
},1000)
const p1 = new Promise( (resolve,reject) => {
setTimeout(() => {
reject(new Error('fail'))
console.log('3s') // console.log語句仍然會執行,並且在reject()異步函數 前執行
},3000)
})
const p2 = new Promise( (resolve,reject) => {
setTimeout( () => {
return resolve(p1) // 一般都在這裏加return,這樣後面的代碼就不會執行,防止意外!!
console.log('1s')
}, 1000 )
})
p2.then(res => console.log(res)) // 並沒有執行
.catch(error => console.log(error))
// 注意: p2.then(res => console.log(....))並沒有執行,因爲p2的狀態變成了p1的狀態,是rejected
// p2.then(res => console.log(res,'fulfilled'), res => console.log(res,'rejected'))
// 實際執行的是上面的 第二個回調函數
}
解析:
(1) p1 是一個promise對象,3s後狀態變爲rejected
(2) p2 是一個promise對象,狀態在 1s 後改變,但是P2的resolvef方法返回的是p1,p1是promise對象
導致p2的狀態由p1決定,即 p1的狀態傳遞給p2
(3) P2會等待P1的狀態改變爲 fulfilled或者reject,P1狀態改變後,P2的回調函數會立刻執行 ( --!!!重要!!!-- )
( 所以1s的時候,.then方法並沒有輸出內容 )
(並且3s後,p2的狀態不是fulfilled,而是 rejeced,即是p1的狀態 )
(4) 又過了2s,p1的狀態變爲 rejected,導致觸發 .catch 回調函數
- 一般來說,調用resolve或reject以後,Promise 的使命就完成了,後繼操作應該放到then方法裏面,而不應該直接寫在resolve或reject的後面。所以,最好在它們前面加上return語句,這樣就不會有意外。
Promise.prototype.then() 方法
promise實例有.then方法,是定義在原型對象 Promise.prototype 上的
- then() 方法的作用是爲 promise實例添加狀態改變後的回調函數
- then() 方法的參數是兩個回掉函數,第一個是 resolved 狀態的回調函數,第二個是rejected狀態的回調函數 ( 第二個參數可選,一般都不用,而用 catch()方法捕獲錯誤 )
- then() 方法返回的是新的promise實例,因此可以採用鏈式寫法
Promise.prototype.then()方法的鏈式調用---------(重要)
採用鏈式的then,可以指定一組按照次序調用的回調函數。
- 這時,前一個回調函數,有可能返回的還是一個Promise對象(即有異步操作),這時後一個回調函數,就會等待該Promise對象的狀態發生變化,纔會被調用。
componentDidMount() {
let i = 0
setInterval(() => {
console.log(`經過了${++i}s`)
},1000)
const lian1 = new Promise( (resolve,reject) => {
return setTimeout(() => {
resolve('2s的promise的fulfilled狀態返回值')
},2000)
})
lian1
.then(res => console.log(res))
.then( res => {
return new Promise( (resolve,reject) => {
return setTimeout(() => {
return reject('3s的promise的rejected狀態返回值')
},1000)
})
})
.then(res => console.log(res,'reject'), res => console.log(res, 'reject'))
}
// 經過了1s
// 經過了2s
// 2s的promise的fulfilled狀態返回值
// 經過了3s
// 3s的promise的fulfilled狀態返回值 reject
// 經過了4s
Promise.prototype.catch()
Promise.prototype.catch() 是 .then(null, rejection) 的別名,用於指定發生錯誤時的回調函數
- 如果promise實例對象的狀態變爲rejected,就會觸發 catch() 方法指定的回調函數
- 如果 .then() 方法指定的回調函數在運行中拋出錯誤,也會被 catch() 方法捕獲
- promise對象的錯誤具有冒泡性質,會一直向後傳遞,直到被捕獲爲止
( 也就是說錯誤總是會被下一個catch語句捕獲 ) - 一般來說,不要在.then()方法中定義rejected狀態的回調函數,而總是使用 .catch()方法
- 一般總是建議,promise對象後要跟 catch()方法,這樣可以處理 promise內部發生法的錯誤,catch() 方法返回的還是promise對象,因此後面還可以接着調用 then() 方法
- catch() 方法中還能再拋錯誤,如果 catch()方法拋出錯誤後,後面沒有catch()方法,錯誤就不會被捕獲,也不會傳遞到外層。
如果catch()方法拋出錯誤後,後面有then()方法,會照常執行,後面有catch()方法,錯誤還會被再一次捕獲
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));
例子
getJSON('/post/1.json')
.then(function(post) {
return getJSON(post.commentURL);
})
.then(function(comments) {
// some code
})
.catch(function(error) {
// 處理前面三個Promise產生的錯誤
});
上面代碼中,一共有三個 Promise 對象:一個由getJSON產生,兩個由then產生。
它們之中任何一個拋出的錯誤,都會被最後一個catch捕獲。
promise.prototype.finally()
promise.prototype.finally()方法用於指定不管promise對象最後的狀態如何,都會執行的操作
- finally() 方法的回調函數,不接受任何參數。
( 這就意味着,無法知道前面pormise實例對象最後的狀態是fulfilled還是rejected,也就是說,finally()函數中的操作與狀態無關,不依賴promise對象執行的結果 ) - finally總是會返回之前的值
promise
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
上面代碼中,不管promise最後的狀態,在執行完then或catch指定的回調函數以後,
都會執行finally方法指定的回調函數。
Promise.all() -------- 注意,不在原型對象上
Promise.all() 方法用於將多個promise實例,包裝成一個新的promise實例
- promise.all() 方法的參數可以不是數組,但是必須具有 iterator 接口,且返回的每個成員都是promise實例 ( 具有iterator接口,就是可遍歷的數據結構,可以被 for...of 遍歷 )
- 注意:如果作爲參數的promise實例
( 即Promise.all()實例子是rejected狀態 )
自己定義了catch()方法,就不會觸發Promise.all()實例的 catch() 方法
const p = Promise.all( [p1, p2, p3] );
上面代碼中,promise.all()方法,接受一個數組作爲參數,p1, p2, p3都是promise實例
promise.all() 方法的參數可以不是數組,但是必須具有 iterator 接口
p的狀態由 p1, p2, p3決定,分兩種情況
(1) 只有p1,p2,p3的狀態都變爲 fulfilled, p的狀態纔會變爲 fulfilled
此時,p1,p2,p3的返回值組成一個數組,傳遞給p的回調函數
(2) 只要p1,p2,p3中有一個被 rejected,p的狀態就變成rejected
此時,第一個被rejected的實例的返回值,會傳給p的回調函數
例子
情況1:
// a,b,c都是promise實例對象
// 當a,b,c都是fulfilled狀態時, p 纔是fulfilled狀態,纔會觸發then的resolved狀態的回調函數
// p 的回調函數的參數,是a,b,c都變爲resolved狀態時的返回值組成的數組
componentDidMount() {
let a = new Promise((resolve, reject) => {
return resolve(1)
})
let b = new Promise((resolve,reject) => {
return resolve(2)
})
let c = new Promise((resolve,reject) => {
return resolve(3)
})
const p = Promise.all([a,b,c]) // a,b,c都是promise實例對象
p.then(res => console.log(res)) // 輸出 [1, 2, 3]
}
情況2:
// a,b,c都是promise實例對象
// 當a,b,c中有一個是rejected狀態時,p的狀態就是rejected狀態,
// p 的回調函數的參數,是最先被rejected的Promse實例的返回值
componentDidMount() {
let a = new Promise((resolve, reject) => {
// return resolve(1)
return reject(new Error('錯誤來自promise----a'))
})
let b = new Promise((resolve,reject) => {
// return resolve(2)
return reject(new Error('錯誤來自promise----b'))
})
let c = new Promise((resolve,reject) => {
return resolve(3)
})
const p = Promise.all([a,b,c]) // p是rejected時,p的回調函數的參數是最先rejected的實例返回值
p.then(res => console.log(res))
.catch(err => console.log(err)) // 輸出 Error: 錯誤來自promise----a
}
特殊情況
const p1 = new Promise((resolve, reject) => {
resolve('hello');
})
.then(result => result)
.catch(e => e);
const p2 = new Promise((resolve, reject) => {
throw new Error('報錯了');
})
.then(result => result)
.catch(e => e);
Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 報錯了]
上面代碼中,p1會resolved,p2首先會rejected,
但是p2有自己的catch方法,該方法返回的是一個新的 Promise 實例,p2指向的實際上是這個實例。
該實例執行完catch方法後,也會變成resolved,導致Promise.all()方法參數裏面的兩個實例都會resolved,
因此會調用then方法指定的回調函數,而不會調用catch方法指定的回調函數。
Promise.race() --------- 對比Promise.all()
Promise.race()方法的作用同樣是將多個promise對象實例包裝成新的promise實例
- race是賽跑,率先的意思
- 規則: 如果p1, p2, p3 中實例對象的狀態有一個率先改變, p的狀態就會跟這改變 ( 無論是變爲fulfilled狀態,還是變爲 rejected狀態 ),p的回調函數的參數,是最先改變的那個promise實例的返回值
const p = Promise.race([ // Promise.race() 只要有一個參數狀態改變,p的狀態就是跟着改變
fetch('/resource-that-may-take-a-while'), // fetch返回的是promise對象
new Promise(function (resolve, reject) { // 5s後變爲rejected狀態
setTimeout(() => reject(new Error('request timeout')), 5000)
})
]);
p
.then(console.log)
.catch(console.error);
解析:
fetch在5s鍾內請求成功,p變成fulfilled狀態,觸發p的then()方法
fetch在5s中內請求失敗,p變爲rejected狀態,觸發p的catch()方法
Promise.resolve()
promise.resolve()可以將對象轉換爲promise對象
- promise.resolve('foo') 等價於
new Promise(resolve => resolve('foo'))
Promise.resolve('foo')
// 等價於
new Promise(resolve => resolve('foo'))
Promise.resolve()方法的 參數
總結:
Promise.resolve()的參數無論是什麼類型 ( 或者不帶參數,或者參數本來就是一個promise對象,或者參數是一個thenable對象,或者參數是原始值,或者參數是普通對象 )本質上Promise.resolve()都會把參數轉化爲Promise對象,只是狀態分情況而已,比如:如果參數是thenable對象,會立即執行thenable對象的then方法,狀態當然後then方法中的函數決定,從而決定Promise.resolve()返回的promise對象的狀態
Promise.resolve()方法的參數分爲四種情況
- 參數是一個promise實例對象
如果Promise.resolve()方法的參數是一個( 實例對象 ),那麼Promise.resolve()方法將原封不動的( 返回這個實例對象 )
componentDidMount() {
const foo = new Promise( (resolve, reject) => {
return resolve('foo是一個promise實例對象')
})
const p = Promise.resolve(foo) // Promise.resolve()的參數是一個promise實例
console.log( p );
// 輸出: Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: "foo是一個promise實例對象"}
}
- 參數是一個thenable 對象
如果Promise.resolve()方法的參數是 ( thenable對象 ),那麼Promise.resolve()方法會將這個對象轉化爲promise對象,然後立刻執行 thenable對象的 then 方法
什麼是 thenable 對象 ?
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
// 如果Promise.resolve()的參數是 thenable對象, Promise.resolve()方法會把thenable對象轉化爲promise對象
// 然後立刻執行thenable對象的then方法
componentDidMount() {
const thenable = { // thenable對象,裏面有then方法
then: (resolve,reject) => resolve('這是thenable對象')
}
Promise.resolve(thenable) // 參數是thenable對象,立刻執行thenable對象的then方法
.then(res => console.log(res)) // thenable對象的的狀態是fulfilled,輸出其返回值
}
// 輸出:這是thenable對象
- 參數不是thenable對象,或者根本不是一個對象
如果參數是一個原始類型的值( 數字,字符串,布爾值 ),或者是一個不具有then方法的對象,則Promise.resolve()方法返回一個新的promise對象,狀態是fulfilled
componentDidMount() {
const str = 'abc'
const foo = Promise.resolve(str)
// 參數是原始類型的值,Promise.resolve()方法會返回一個promise對象,狀態是resolved
foo.then(res => console.log(res)) // 所以該回調會執行
}
- 不帶參數
當Promise.resolve()方法不帶參數是,直接返回一個promise對象,狀態是resolved
Promise.reject()方法
Promise.reject()方法返回一個promise實例對象,狀態是rejected
- 和Promise.resolve()類似,只是Promise.rejected()方法的狀態一定是rejected
const p = Promise.reject('出錯了');
p.then(null, function (s) {
console.log(s)
});
// 出錯了
// 等同於
const p = new Promise((resolve, reject) => reject('出錯了'))
p.then(null, function (s) {
console.log(s)
});
// 出錯了
promise對象的應用
Promise.try()
const f = () => console.log('now'); // 函數f是一個同步事件
Promise.resolve().then(f); // 通過Promise.resolve()返回一個promise對象,狀態是resolved,f變成異步
console.log('next');
// next 所以會先輸出next,再輸出now
// now
如何讓同步函數同步執行,異步函數異步執行,並且讓他們具有統一的api呢?
-
(1) 使用async函數
await 操作符用於等待一個 Promise 對象, 它只能在異步函數 async function 內部使用.
componentDidMount() {
const funSync1 = () => console.log('我是同步函數1111111')
const funSync2 = () => console.log('我是同步函數2222222')
const funAsync = async () => { // async關鍵字,定義的函數是異步函數,返回promise對象
await funSync1()
}
funAsync().then(funSync2())
console.log('bbbb')
}
// 先把兩個同步函數變成了異步,在異步函數中,先執行funSync1,後執行funSync2
// 使用async關鍵字後,會把同步包裝成的異步函數,按同步方式執行
// 所以最後得到的輸出順序是:
// 我是同步函數1111111
// 我是同步函數2222222
// bbbb
-
(2) 使用 new Promise()
const f = () => console.log('now');
(
() => new Promise(
resolve => resolve(f())
)
)();
console.log('next');
// now
// next
-
(3) Promise.try() 提案
const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next
作者:省局到
鏈接:https://www.jianshu.com/p/4a937870511d
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯繫作者獲得授權並註明出處。