Javascript 非同步程式設計
JS單執行緒的原因
如果多執行緒同時操作了dom,瀏覽器並不知道以誰為準。
優點:安全。
缺點:如果有耗時任務,會出現假死現象。
所以為了解決以上問題,JS有倆種模式
同步模式
程式碼依次執行。
函式的宣告不會入CallStack,呼叫的方法會進入CallStack,執行完成後彈出。可以想象成CallStack就是JS的任務執行表。
如果同步模式遇到耗時操作,可能會卡死,這個時候就需要進行非同步解決。
非同步模式
開啟非同步任務後,繼續執行同步程式碼,後續邏輯用回撥函式處理。
Timeout開啟定時器,進入WebApi後對Timeout說已經完畢,會彈出CallStack,繼續執行下一程式碼。
EventLoop負責呼叫棧和訊息佇列。
當CallStack清空後,EventLoop就會從訊息佇列中取出第一個回撥函式壓棧。
訊息佇列中發生了變化,事件迴圈就會監聽到,會就拿到佇列的第一個拿出來壓棧。
注意JS是單執行緒(執行程式碼的是單執行緒),但瀏覽器不是。JS某些API也不是單執行緒的,例如計時器,單獨開了執行緒。
回撥函式
非同步的本質就是回撥,例如 你根本不知道這個非同步任務何時完成,這個時候就需要一個方法來通知你。
Promise
Promise是個物件。
Promise只有三種狀態,待定 --》 成功或者失敗
// Promise 基本示例
const promise = new Promise(function (resolve, reject) {
// 這裡用於“兌現”承諾
// resolve(100) // 承諾達成
reject(new Error('promise rejected')) // 承諾失敗
})
promise.then(function (value) {
// 即便沒有非同步操作,then 方法中傳入的回撥仍然會被放入佇列,等待下一輪執行
console.log('resolved', value)
}, function (error) {
console.log('rejected', error)
})
console.log('end') 最先執行
注意:及時.then沒有回撥函式也會進入佇列排隊。
new的時候 promise就會被執行了。
Promise 方式的 AJAX
API
users.json
[
{
"name": "zce",
"age": 24
},
{
"name": "alan",
"age": 25
}
]
urls.json
{
"users": "/api/users.json",
"posts": "/api/posts.json"
}
posts.json
[
{
"title": "Hello world",
"body": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
},
{
"title": "25 forever",
"body": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
}
]
Promise 方式的 AJAX
// Promise 方式的 AJAX
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/foo.json').then(function (res) {
console.log(res)
}, function (error) {
console.log(error)
})
Promise 常見誤區
// Promise 常見誤區
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()
})
}
// 巢狀使用 Promise 是最常見的誤區
// ajax('/api/urls.json').then(function (urls) {
// ajax(urls.users).then(function (users) {
// ajax(urls.users).then(function (users) {
// ajax(urls.users).then(function (users) {
// ajax(urls.users).then(function (users) {
// })
// })
// })
// })
// })
Promise 鏈式呼叫
// Promise 鏈式呼叫
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()
})
}
// var promise = ajax('/api/users.json')
// var promise2 = promise.then(
// function onFulfilled (value) {
// console.log('onFulfilled', value)
// },
// function onRejected (error) {
// console.log('onRejected', error)
// }
// )
// console.log(promise2 === promise) false .then後返回全新的promise
ajax('/api/users.json')
.then(function (value) {
console.log(1111)
return ajax('/api/urls.json')
}) // => Promise
.then(function (value) {
console.log(2222)
console.log(value)
return ajax('/api/urls.json')
}) // => Promise
.then(function (value) {
console.log(3333)
return ajax('/api/urls.json')
}) // => Promise
.then(function (value) {
console.log(4444)
return 'foo'
}) // => Promise
.then(function (value) {
console.log(5555)
console.log(value)
})
Promise 異常處理
// Promise 異常處理
function ajax (url) {
return new Promise(function (resolve, reject) {
// foo()
// throw new Error()
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/users11.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
// }, function onRejected (error) {
// console.log('onRejected', error)
// })
// 使用 catch 註冊失敗回撥是更常見的
// ajax('/api/users11.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
// })
// .catch(function onRejected (error) {
// console.log('onRejected', error)
// })
// then(onRejected) 實際上就相當於 then(undefined, onRejected)
// ajax('/api/users11.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
// })
// .then(undefined, function onRejected (error) {
// console.log('onRejected', error)
// })
// 同時註冊的 onRejected 只是給當前 Promise 物件註冊的失敗回撥
// 它只能捕獲到當前 Promise 物件的異常
// ajax('/api/users.json')
// .then(function onFulfilled (value) {
// console.log('onFulfilled', value)
// return ajax('/error-url')
// }, function onRejected (error) {
// console.log('onRejected', error)
// })
// 因為 Promise 鏈條上的任何一個異常都會被一直向後傳遞,直至被捕獲
// 分開註冊的 onRejected 相當於給整個 Promise 鏈條註冊失敗回撥
ajax('/api/users.json')
.then(function onFulfilled (value) {
console.log('onFulfilled', value)
return ajax('/error-url')
}) // => Promise {}
// .catch(function onRejected (error) {
// console.log('onRejected', error)
// })
// 全域性捕獲 Promise 異常,類似於 window.onerror
window.addEventListener('unhandledrejection', event => {
const { reason, promise } = event
console.log(reason, promise)
// reason => Promise 失敗原因,一般是一個錯誤物件
// promise => 出現異常的 Promise 物件
event.preventDefault()
}, false)
// Node.js 中使用以下方式
// process.on('unhandledRejection', (reason, promise) => {
// console.log(reason, promise)
// // reason => Promise 失敗原因,一般是一個錯誤物件
// // promise => 出現異常的 Promise 物件
// })
catch和沒有catch 的區別
鏈式呼叫下
常用 Promise 靜態方法
// 常用 Promise 靜態方法
function ajax (url) {
return new Promise(function (resolve, reject) {
// foo()
// throw new Error()
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()
})
}
// Promise.resolve('foo')
// .then(function (value) {
// console.log(value)
// })
// new Promise(function (resolve, reject) {
// resolve('foo')
// })
// 如果傳入的是一個 Promise 物件,Promise.resolve 方法原樣返回
// var promise = ajax('/api/users.json')
// var promise2 = Promise.resolve(promise)
// console.log(promise === promise2) true
// 如果傳入的是帶有一個跟 Promise 一樣的 then 方法的物件,
// Promise.resolve 會將這個物件作為 Promise 執行
// Promise.resolve({
// then: function (onFulfilled, onRejected) {
// onFulfilled('foo')
// }
// })
// .then(function (value) {
// console.log(value)
// })
// Promise.reject 傳入任何值,都會作為這個 Promise 失敗的理由
// Promise.reject(new Error('rejected'))
// .catch(function (error) {
// console.log(error)
// })
Promise.reject('anything')
.catch(function (error) {
console.log(error)
})
Promise 並行執行
如果沒有互相依賴,就可以並行請求,節省時間。
// Promise 並行執行
function ajax (url) {
return new Promise(function (resolve, reject) {
// foo()
// throw new Error()
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/users.json')
// ajax('/api/posts.json')
// var promise = Promise.all([
// ajax('/api/users.json'),
// ajax('/api/posts.json')
// ])
// promise.then(function (values) {
// console.log(values)
// }).catch(function (error) {
// console.log(error)
// })
// ajax('/api/urls.json')
// .then(value => {
// const urls = Object.values(value)
// const tasks = urls.map(url => ajax(url))
// return Promise.all(tasks)
// })
// .then(values => {
// console.log(values)
// })
// Promise.race 實現超時控制
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)
})
微任務 巨集任務
// 微任務
console.log('global start')
// setTimeout 的回撥是 巨集任務,進入回撥佇列排隊
setTimeout(() => {
console.log('setTimeout')
}, 0)
// Promise 的回撥是 微任務,本輪呼叫末尾直接執行
Promise.resolve()
.then(() => {
console.log('promise')
})
.then(() => {
console.log('promise 2')
})
.then(() => {
console.log('promise 3')
})
console.log('global end')
settimeout是巨集任務,巨集任務有了新的巨集任務後 會到最後尾繼續排隊,微任務會
但Promise和MutaitionObserver還有node的process.nexttick是微任務,在本輪呼叫的末尾就執行了。
generator
promise還是會有.then多層,可讀性還是沒有同步程式碼那麼高,這個時候我們還有更優的解決方案,generator和 Async / Await。generator可以作為了解。今後開發都會 Async / Await為主了。
// 生成器函式回顧
function * foo () {
console.log('start')
try {
const res = yield 'foo'
console.log(res)
} catch (e) {
console.log(e)
}
}
const generator = foo() 不會立即執行
const result = generator.next()
/// next後執行 執行到yield位置 把yield後面的值返回出去 foo函式就會暫停下拉
//next會返回一個 yield返回的值
console.log(result)
// generator.next('bar') 在yield暫定的位置繼續執行,這裡的bar會作為yield的返回值
generator.throw(new Error('Generator error'))
Generator 配合 Promise 的非同步方案
// Generator 配合 Promise 的非同步方案
function ajax (url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response)
} else {
reject(new Error(xhr.statusText))
}
}
xhr.send()
})
}
function * main () {
try {
const users = yield ajax('/api/users.json')
console.log(users)
const posts = yield ajax('/api/posts.json')
console.log(posts)
const urls = yield ajax('/api/urls11.json')
console.log(urls)
} catch (e) {
console.log(e)
}
}
function co (generator) {
const g = generator()
function handleResult (result) {
if (result.done) return // 生成器函式結束
result.value.then(data => {
handleResult(g.next(data))
}, error => {
g.throw(error)
})
}
handleResult(g.next())
}
co(main) 15年前比較流行
// const result = g.next()
// result.value.then(data => {
// const result2 = g.next(data)
// if (result2.done) return
// result2.value.then(data => {
// const result3 = g.next(data)
// if (result3.done) return
// result3.value.then(data => {
// g.next(data)
// })
// })
// })
Async / Await 語法糖
// Async / Await 語法糖
function ajax (url) {
return new Promise((resolve, reject) => {
var xhr = new XMLHttpRequest()
xhr.open('GET', url)
xhr.responseType = 'json'
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response)
} else {
reject(new Error(xhr.statusText))
}
}
xhr.send()
})
}
function co (generator) {
const g = generator()
function handleResult (result) {
if (result.done) return // 生成器函式結束
result.value.then(data => {
handleResult(g.next(data))
}, error => {
g.throw(error)
})
}
handleResult(g.next())
}
async function main () {
try {
const users = await ajax('/api/users.json')
console.log(users)
const posts = await ajax('/api/posts.json')
console.log(posts)
const urls = await ajax('/api/urls.json')
console.log(urls)
} catch (e) {
console.log(e)
}
}
// co(main)
const promise = main()
promise.then(() => {
console.log('all completed')
})
相關文章
- JavaScript非同步程式設計JavaScript非同步程式設計
- 探索Javascript非同步程式設計JavaScript非同步程式設計
- 你好,JavaScript非同步程式設計—- 理解JavaScript非同步的美妙JavaScript非同步程式設計
- 你好,JavaScript非同步程式設計---- 理解JavaScript非同步的美妙JavaScript非同步程式設計
- python 網路程式設計----非阻塞或非同步程式設計Python程式設計非同步
- [譯] 非同步程式設計:阻塞與非阻塞非同步程式設計
- 向非程式設計師解釋JavaScript程式設計師JavaScript
- 向非程式設計師解釋 JavaScript程式設計師JavaScript
- Javascript中的非同步程式設計JavaScript非同步程式設計
- JavaScript非同步程式設計筆記JavaScript非同步程式設計筆記
- 前端- JavaScript非同步程式設計Promise前端JavaScript非同步程式設計Promise
- Javascript非同步程式設計總結JavaScript非同步程式設計
- 談談 JavaScript 非同步程式設計JavaScript非同步程式設計
- JavaScript 非同步程式設計入門JavaScript非同步程式設計
- Socket程式設計中的同步、非同步、阻塞和非阻塞(轉)程式設計非同步
- JavaScript非同步程式設計:Generator與AsyncJavaScript非同步程式設計
- JavaScript非同步程式設計-基礎篇JavaScript非同步程式設計
- Javascript非同步程式設計的前世今生JavaScript非同步程式設計
- javascript非同步程式設計(一)-現狀JavaScript非同步程式設計
- JavaScript非同步程式設計助手:Promise模式JavaScript非同步程式設計Promise模式
- JavaScript非同步程式設計的Promise模式JavaScript非同步程式設計Promise模式
- JavaScript非同步程式設計:非同步的資料收集方法JavaScript非同步程式設計
- JavaScript非同步程式設計的6種方法JavaScript非同步程式設計
- javascript 非同步程式設計的5種方式JavaScript非同步程式設計
- Javascript中常見的非同步程式設計模型JavaScript非同步程式設計模型
- 說一說javascript的非同步程式設計JavaScript非同步程式設計
- 前端-JavaScript非同步程式設計async函式前端JavaScript非同步程式設計函式
- JavaScript非同步程式設計 | 掘金技術徵文JavaScript非同步程式設計
- Javascript非同步程式設計的4種方法JavaScript非同步程式設計
- JavaScript 單執行緒之非同步程式設計JavaScript執行緒非同步程式設計
- 深入解析JavaScript非同步程式設計:Generator與AsyncJavaScript非同步程式設計
- 我瞭解到的JavaScript非同步程式設計JavaScript非同步程式設計
- javascript非同步程式設計幾種方法簡介JavaScript非同步程式設計
- 【進階之路】併發程式設計(三)-非阻塞同步機制程式設計
- JavaScript深入淺出非同步程式設計二、promise原理JavaScript非同步程式設計Promise
- JavaScript非同步程式設計–Generator函式、async、awaitJavaScript非同步程式設計函式AI
- JavaScript非同步程式設計大冒險: Async/AwaitJavaScript非同步程式設計AI
- JavaScript非同步程式設計(1)- ECMAScript 6的Promise物件JavaScript非同步程式設計Promise物件