單線程
js是單線程模式的,試想在js當(dāng)中同時(shí)有多個(gè)線程,其中有一個(gè)線程修改了某一個(gè)dom元素,而另外一個(gè)線程同時(shí)刪除了這個(gè)元素,瀏覽器就無法確定以那一個(gè)線程結(jié)果為準(zhǔn),為了避免這種線問題,從一開始js就被設(shè)計(jì)為了單線程模式。這里單線程是js運(yùn)行環(huán)境執(zhí)行代碼的線程只有一個(gè),當(dāng)有多個(gè)任務(wù)的時(shí)候,排隊(duì)等待依次完成。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn):安全,簡(jiǎn)單
缺點(diǎn):假如遇到一個(gè)特別耗時(shí)的任務(wù),其他任務(wù)就被堵塞,整個(gè)程序出現(xiàn)假死情況。
同步/異步
為了解決這種問題,js將執(zhí)行模式分為同步(synchronous)異步(asynchronous)
同步模式(synchronous)
指的就是我們代碼當(dāng)中的任務(wù)依次執(zhí)行,后一個(gè)任務(wù)必須等待前一個(gè)任務(wù)結(jié)束才能執(zhí)行,在單線程模式下,我們大多數(shù)任務(wù)會(huì)以同步模式去運(yùn)行.
簡(jiǎn)單例子:
console.log('global begin')
function bar () {
console.log('bar task')
}
function foo () {
console.log('foo task')
}
bar()
foo()
console.log('global end')
//global begin
//bar task
//bar task
//global end
這種就是純同步代碼,就是順序執(zhí)行,如果遇到特別耗時(shí)的任務(wù),就會(huì)堵塞。這種阻塞對(duì)于用戶而言就意味著界面會(huì)卡頓,或者卡死,所以為了解決這個(gè)問題出現(xiàn)了異步模式。
異步模式(asynchronous)
與同步模式不同,異步模式的執(zhí)行是不會(huì)等待這個(gè)任務(wù)的結(jié)束才會(huì)執(zhí)行下一個(gè)任務(wù),對(duì)于耗時(shí)操作都是開啟過后就放到異步隊(duì)列立即往后執(zhí)行下一個(gè)任務(wù),內(nèi)部我們這個(gè)耗時(shí)任務(wù)完成過后就會(huì)自動(dòng)執(zhí)行我們這里傳入的回調(diào)函數(shù)。
優(yōu)點(diǎn):解決同步線程堵塞的問題
缺點(diǎn):代碼執(zhí)行的順序的混亂,不像同步代碼那樣是順序執(zhí)行的了。
簡(jiǎn)單例子:
console.log('global begin')
setTimeout(function timer1 () {
console.log('timer1 invoke')
}, 1800)
setTimeout(function timer2 () {
console.log('timer2 invoke')
setTimeout(function inner () {
console.log('inner invoke')
}, 1000)
}, 1000)
console.log('global end')
// global begin
// global end
// timer2 invoke
// timer1 invoke
// inner invoke
這里也是簡(jiǎn)單介紹開啟異步的 像setTimeout是宏任務(wù),promise是微任務(wù),都會(huì)開啟異步模式。有需要的可以再去了解下js的eventLoop(js事件循環(huán)機(jī)制),這里重點(diǎn)講處理異步方法。
回調(diào)函數(shù)
就是你想做的事情,你也知道這件事你應(yīng)該怎末做,但是不知道依賴的任務(wù)什么時(shí)候才完成,任務(wù)什么時(shí)候才會(huì)執(zhí)行到你這個(gè)方法,最好的方法就是把這個(gè)任務(wù)寫到一個(gè)函數(shù)中,交給執(zhí)行者,他是知道這個(gè)任務(wù)什么時(shí)候結(jié)束的,他就可以在任務(wù)結(jié)束過后幫你去執(zhí)行,你想要做的事情,那這個(gè)想要做的事情其實(shí)就可以理解為回調(diào)函數(shù)。
拿我們?cè)谀贸绦虍?dāng)中的ajax請(qǐng)求為例:當(dāng)我們調(diào)用ajax操作,目的就是為了拿到請(qǐng)求結(jié)果過后去做一些事情,例如我們將其顯示到界面上,但是這個(gè)請(qǐng)求什么時(shí)候能完成我們并不知道,所以說我們需要把得到結(jié)果要去執(zhí)行的任務(wù)定義到一個(gè)函數(shù)中,然后內(nèi)部的ajax請(qǐng)求到數(shù)據(jù)過后,它會(huì)自動(dòng)執(zhí)行這個(gè)任務(wù)。
這種由調(diào)用者定義,交給執(zhí)行者執(zhí)行的函數(shù),稱之為回調(diào)函數(shù)。
**
簡(jiǎn)單例子:
function foo (callback) {
setTimeout(function () {
callback()
}, 3000)
}
foo(function () {
console.log('這就是一個(gè)回調(diào)函數(shù)')
console.log('調(diào)用者定義這個(gè)函數(shù),執(zhí)行者執(zhí)行這個(gè)函數(shù)')
console.log('其實(shí)就是調(diào)用者告訴執(zhí)行者異步任務(wù)結(jié)束后應(yīng)該做什么')
})
假如你請(qǐng)求a數(shù)據(jù),b在a成功返回的時(shí)候執(zhí)行,c依賴b的成功執(zhí)行,d依賴c.....如果是普通的會(huì)容易寫成回調(diào)函數(shù)的嵌套,多層的嵌套就是回調(diào)地獄。
而promise就是一種更優(yōu)的解決方案。
promise
- promise實(shí)際上就是一個(gè)對(duì)象,用來去表示一個(gè)異步任務(wù),最終結(jié)束過后,他是成功還是失敗
就像是內(nèi)部對(duì)外界做出一個(gè)承諾,一開始這個(gè)承諾是一個(gè)待定的狀態(tài) Pending,最后有可能成功 fulfilled,也有可能失敗 Rejected。
- 承諾狀態(tài)明確過后,不管是成功還是失敗,都會(huì)有相對(duì)應(yīng)的任務(wù)會(huì)被自動(dòng)執(zhí)行,而且這種承諾會(huì)有很明顯的特點(diǎn),一點(diǎn)明確了結(jié)果過后,不可更改。
例如:你需要我去幫你發(fā)送一次ajax請(qǐng)求,其實(shí)就可以理解為,我承諾幫你請(qǐng)求一個(gè)地址,這個(gè)請(qǐng)求有可能成功,然后調(diào)用 onFulfiled 的回調(diào),如果失敗就會(huì)調(diào)用 onRejected 的回調(diào)。
promise基本用法
const promise = new Promise(function(resolve, reject) {
// "兌現(xiàn)" 承諾的邏輯
// 承諾--------達(dá)成
resolve(100)
//承諾--------失敗
//reject(new Error('promise err'))
})
promise.then(function(value) {
console.log("resoled",value)
},function(err) {
console.log("rejected", err)
})
//resoled 100
你可以分別執(zhí)行下resolve,和reject方法,看下它的執(zhí)行結(jié)果,后面會(huì)具體說promise的resolve和reject。
promise的使用案例
function ajax (url) {
return new Promise (function (resolve, reject) {
let xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = function () {
if(this.status === 200){
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
ajax('../api/users.json').then(function (res) {
console.log(res)
}, function (error) {
console.log(error)
})
Promise常見誤區(qū)
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open('GET',url)
xhr.responseType = 'json'
xhr.onload = function() {
if(this.status === 200) {
resolve(this.response)
}else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
ajax('api/user.json').then(function(urls) {
ajax(urls.users).then(function(users){
ajax(urls.users).then(function(users){
// ... 還是會(huì)形成回調(diào)地獄 promise 沒有了意義
})
})
},function(error) {
console.log(error)
})
就是你用了promise還發(fā)生了回調(diào)嵌套,沒有用then鏈?zhǔn)秸{(diào)用。emm...
promise的鏈?zhǔn)秸{(diào)用
其實(shí)Promise最大的優(yōu)勢(shì)就是可以鏈?zhǔn)秸{(diào)用,這樣就能最大程度的去避免回調(diào)嵌套。
每一個(gè)then方法他實(shí)際上都是在為上一個(gè)then返回的promise對(duì)象添加狀態(tài)明確過后的回調(diào),那這些promise會(huì)依次執(zhí)行,這里添加的這些回調(diào)函數(shù)也就是從前到后依次執(zhí)行。
例子:
function ajax(url) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest()
xhr.open('GET',url)
xhr.responseType = 'json'
xhr.onload = function() {
if(this.status === 200) {
resolve(this.response)
}else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
let promise = ajax('api/user.json')
promise.then(function(value) {
console.log("1111")
return ajax('api/urls.json')
})//-> promise
.then(function(value) {
console.log("2222")
})//-> promise
.then(function(value) {
console.log("3333")
})//-> promise
.then(function(value) {
console.log("4444")
})//-> promise
.then(function(value) {
console.log("5555")
})
let promise2 = promise.then(function(res) {
console.log(res)
},function(error) {
console.log(error)
})
console.log(promise2 === promise) //false
總結(jié):
- 每一個(gè)then方法都是在為上一個(gè)promise添加回調(diào),
- promise的then方法會(huì)返回一個(gè)全新的promise對(duì)象(就可以使用鏈?zhǔn)秸{(diào)用的方式去調(diào)用then方法)
- 后面的then方法就是在為上一個(gè)then返回的promise注冊(cè)回調(diào)
- 前面方法中回調(diào)函數(shù)的返回值 會(huì)作為后面then方法回調(diào)的參數(shù)
- 如果回調(diào)中返回的是一個(gè)promise對(duì)象,那后面的then方法的回調(diào)會(huì)等待它的結(jié)束
promise異常處理
- Promise執(zhí)行失敗,會(huì)返回onRejected這個(gè)回調(diào)函數(shù)
- 如果是在Promise執(zhí)行的過程中,出現(xiàn)了異常,或者是我們手動(dòng)拋出了一個(gè)異常,那onRejected也會(huì)被執(zhí)行
我們也可以使用promise的cache方法來注冊(cè)onRejected.
例子:
function ajax(url) {
return new Promise(function(resolve, reject) {
// foo() 可以捕獲到異常
var xhr = new XMLHttpRequest()
xhr.open('GET',url)
xhr.responseType = 'json'
xhr.onload = function() {
if(this.status === 200) {
resolve(this.response)
}else {
reject(new Error(this.statusText))
}
}
xhr.send()
})
}
ajax('api/user.json') //1
.then(function onFulfilled (res) {
console.log("onFulfilled",res)
},function onRejected (error) {
console.log("onRejected",error)
})
ajax('api/user.json') //2
.then(function onFulfilled (res) {
console.log("onFulfilled",res)
})
.catch(function onRejected (error) {
console.log("onRejected",error)
})
/*
1的onRejected 只能捕獲第一個(gè)then方法返回的異常
2 能捕獲整個(gè)promise鏈條任何一個(gè)異常,都會(huì)往下傳遞直至被捕獲
*/
//也能捕獲到異常,catch 就是then方法的別名
// 相當(dāng)于
// ajax('api/user.json')
// .then(function onFulfilled (res) {
// console.log("onFulfilled",res)
// })
// .then(undefined, function onRejected (error) {
// console.log("onRejected",error)
// })
promise的靜態(tài)方法
promise.resolve()
快速的把一個(gè)值轉(zhuǎn)換為一個(gè)promise對(duì)象
// promise.resolve() 作用就是快速把一個(gè)值轉(zhuǎn)化為一個(gè)promise對(duì)象
Promise.resolve("foo")
.then(function(value) {
console.log(value)
})
// 等價(jià)于
new Promise(function(resolve, reject) {
resolve('foo')
})
// 如果primse.resolve()參數(shù)傳一個(gè)promise對(duì)象 他會(huì) 返回原對(duì)象
var promise = ajax('/api/users.json')
var promise2 = Promise.resolve(promise)
console.log(promise === promise2) //true
// 如果我們傳入的是個(gè)對(duì)象,有then方法(他是以額可以被then的對(duì)象),這樣的對(duì)象耶可以作為promise來執(zhí)行
Promise.resolve({
then: function(onFulfilled, onRejected) {
onFulfilled('foo1')
}
})
.then(function(value) {
console.log(value)
})
promise.reject()
快速的創(chuàng)建一個(gè)一定失敗的promise對(duì)象.
// 無論傳入什么參數(shù)都會(huì)作為 這個(gè)promise失敗的原因
Promise.reject(new Error('rejected'))
.catch (function(error) {
console.log(error)
})
//rejected
Promise并行處理
promise.all()
promise.all 允許我們按照異步代碼調(diào)用的順序得到異步代碼執(zhí)行的順序, 接收數(shù)組,可以是promise對(duì)象 或普通值 , 返回是個(gè)promise對(duì)象 后面可以鏈接then,所有成功 promise.all才是成功 有一個(gè)失敗就是失敗。
// 返回一個(gè)新得promise對(duì)象,必須里面的都執(zhí)行完成 才會(huì)完成,
// 必須成功執(zhí)行 all才會(huì)成功執(zhí)行,有一個(gè)任務(wù)失敗了 這個(gè)promise以失敗結(jié)束
var promise = Promise.all([
ajax('/api/user.json'),
ajax('/api/posts.json')
])
// 數(shù)組里包含每個(gè)異步任務(wù)執(zhí)行的結(jié)果
promise.then(function(values) {
console.log(values)
}).catch(function onRejected (error) {
console.log("onRejected",error)
})
注意:promise.all() 等待所有的任務(wù)結(jié)束后才結(jié)束。
promise.race()
Promise.race() 跟著所有任務(wù)當(dāng)中第一個(gè)任務(wù)一起結(jié)束。
const request = ajax('/api/posts.json')
const timeout = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('timeout')), 500)
})
Promise.race({
request,
timeout
})
.then( value => {
console.log(value)
})
.catch(error => {
console.log(error)
})
promise執(zhí)行時(shí)序
Promise 當(dāng)中并沒有任何的異步操作,那他的回調(diào)函數(shù)仍然會(huì)進(jìn)入到回調(diào)隊(duì)列當(dāng)中去排隊(duì),也就是說我們必須要等待當(dāng)前所有的同步代碼執(zhí)行完成,過后才會(huì)去執(zhí)行 Promise 當(dāng)中的回調(diào)。
js的執(zhí)行次序 主線程->微任務(wù)隊(duì)列->宏任務(wù)隊(duì)列。
在主線程執(zhí)行完后 會(huì)立即執(zhí)行微任務(wù)隊(duì)列(從頭到尾)中間遇到宏觀任務(wù)就加到宏觀任務(wù)隊(duì)列的尾部,執(zhí)行完微隊(duì)列,再執(zhí)行宏任務(wù)。
而promise就是微任務(wù),setTimeout等是宏任務(wù)。
看個(gè)例子:
console.log('global start')
Promise.resolve()
.then(() => {
console.log('promise')
})
console.log('global end')
/**
* global start
* global end
* promise
* */
//=================================
console.log('global start')
Promise.resolve()
.then(() => {
console.log('promise 1')
})
.then(() => {
console.log('promise 2')
})
.then(() => {
console.log('promise 3')
})
.then(() => {
console.log('promise 4')
})
console.log('global end')
/**
* global start
* global end
* promise 1
* promise 2
* promise 3
* promise 4
* */
//==============================
console.log('global start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve()
.then(() => {
console.log('promise 1')
})
.then(() => {
console.log('promise 2')
})
.then(() => {
console.log('promise 3')
})
.then(() => {
console.log('promise 4')
})
console.log('global end')
/**
* global start
* global end
* promise 1
* promise 2
* promise 3
* promise 4
* setTimeout
* */
完結(jié)撒花~??
讀書不覺已春深,一寸光陰一寸金