本篇內(nèi)容主要講解“JS異步編程方案有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“JS異步編程方案有哪些”吧!
成都創(chuàng)新互聯(lián)是專業(yè)的沈北新網(wǎng)站建設(shè)公司,沈北新接單;提供成都網(wǎng)站設(shè)計、成都網(wǎng)站制作,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行沈北新網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
一、同步與異步
我們可以通俗理解為異步就是一個任務(wù)分成兩段,先執(zhí)行***段,然后轉(zhuǎn)而執(zhí)行其他任務(wù),等做好了準(zhǔn)備,再回過頭執(zhí)行第二段。排在異步任務(wù)后面的代碼,不用等待異步任務(wù)結(jié)束會馬上運行,也就是說,異步任務(wù)不具有”堵塞“效應(yīng)。比如,有一個任務(wù)是讀取文件進行處理,異步的執(zhí)行過程就是下面這樣
這種不連續(xù)的執(zhí)行,就叫做異步。相應(yīng)地,連續(xù)的執(zhí)行,就叫做同步
"異步模式"非常重要。在瀏覽器端,耗時很長的操作都應(yīng)該異步執(zhí)行,避免瀏覽器失去響應(yīng),***的例子就是Ajax操作。在 二、回調(diào)函數(shù)(Callback) 回調(diào)函數(shù)是異步操作最基本的方法。以下代碼就是一個回調(diào)函數(shù)的例子: 但是回調(diào)函數(shù)有一個致命的弱點,就是容易寫出回調(diào)地獄(Callback hell)。假設(shè)多個請求存在依賴性,你可能就會寫出如下代碼: 回調(diào)函數(shù)的優(yōu)點是簡單、容易理解和實現(xiàn),缺點是不利于代碼的閱讀和維護,各個部分之間高度耦合,使得程序結(jié)構(gòu)混亂、流程難以追蹤(尤其是多個回調(diào)函數(shù)嵌套的情況),而且每個任務(wù)只能指定一個回調(diào)函數(shù)。此外它不能使用 try catch 捕獲錯誤,不能直接 return。 三、事件監(jiān)聽 這種方式下,異步任務(wù)的執(zhí)行不取決于代碼的順序,而取決于某個事件是否發(fā)生。 下面是兩個函數(shù)f1和f2,編程的意圖是f2必須等到f1執(zhí)行完成,才能執(zhí)行。首先,為f1綁定一個事件(這里采用的jQuery的寫法) 上面這行代碼的意思是,當(dāng)f1發(fā)生done事件,就執(zhí)行f2。然后,對f1進行改寫: 上面代碼中,f1.trigger('done')表示,執(zhí)行完成后,立即觸發(fā)done事件,從而開始執(zhí)行f2。 這種方法的優(yōu)點是比較容易理解,可以綁定多個事件,每個事件可以指定多個回調(diào)函數(shù),而且可以"去耦合",有利于實現(xiàn)模塊化。缺點是整個程序都要變成事件驅(qū)動型,運行流程會變得很不清晰。閱讀代碼的時候,很難看出主流程。 四、發(fā)布訂閱 我們假定,存在一個"信號中心",某個任務(wù)執(zhí)行完成,就向信號中心"發(fā)布"(publish)一個信號,其他任務(wù)可以向信號中心"訂閱"(subscribe)這個信號,從而知道什么時候自己可以開始執(zhí)行。這就叫做"發(fā)布/訂閱模式"(publish-subscribe pattern),又稱"觀察者模式"(observer pattern)。 首先,f2向信號中心jQuery訂閱done信號。 然后,f1進行如下改寫: 上面代碼中,jQuery.publish('done')的意思是,f1執(zhí)行完成后,向信號中心jQuery發(fā)布done信號,從而引發(fā)f2的執(zhí)行。 f2完成執(zhí)行后,可以取消訂閱(unsubscribe) 這種方法的性質(zhì)與“事件監(jiān)聽”類似,但是明顯優(yōu)于后者。因為可以通過查看“消息中心”,了解存在多少信號、每個信號有多少訂閱者,從而監(jiān)控程序的運行。 五、Promise/A+ Promise本意是承諾,在程序中的意思就是承諾我過一段時間后會給你一個結(jié)果。 什么時候會用到過一段時間?答案是異步操作,異步是指可能比較長時間才有結(jié)果的才做,例如網(wǎng)絡(luò)請求、讀取本地文件等 1.Promise的三種狀態(tài) Pending----Promise對象實例創(chuàng)建時候的初始狀態(tài) Fulfilled----可以理解為成功的狀態(tài) Rejected----可以理解為失敗的狀態(tài) 這個承諾一旦從等待狀態(tài)變成為其他狀態(tài)就永遠(yuǎn)不能更改狀態(tài)了,比如說一旦狀態(tài)變?yōu)?resolved 后,就不能再次改變?yōu)镕ulfilled 當(dāng)我們在構(gòu)造 Promise 的時候,構(gòu)造函數(shù)內(nèi)部的代碼是立即執(zhí)行的 2.promise的鏈?zhǔn)秸{(diào)用 每次調(diào)用返回的都是一個新的Promise實例(這就是then可用鏈?zhǔn)秸{(diào)用的原因) 如果then中返回的是一個結(jié)果的話會把這個結(jié)果傳遞下一次then中的成功回調(diào) 如果then中出現(xiàn)異常,會走下一個then的失敗回調(diào) 在 then中使用了return,那么 return 的值會被Promise.resolve() 包裝(見例1,2) then中可以不傳遞參數(shù),如果不傳遞會透到下一個then中(見例3) catch 會捕獲到?jīng)]有捕獲的異常 接下來我們看幾個例子: Promise不僅能夠捕獲錯誤,而且也很好地解決了回調(diào)地獄的問題,可以把之前的回調(diào)地獄例子改寫為如下代碼: 它也是存在一些缺點的,比如無法取消 Promise,錯誤需要通過回調(diào)函數(shù)捕獲。 六、生成器Generators/ yield Generator 函數(shù)是 ES6 提供的一種異步編程解決方案,語法行為與傳統(tǒng)函數(shù)完全不同,Generator ***的特點就是可以控制函數(shù)的執(zhí)行。 語法上,首先可以把它理解成,Generator 函數(shù)是一個狀態(tài)機,封裝了多個內(nèi)部狀態(tài)。 Generator 函數(shù)除了狀態(tài)機,還是一個遍歷器對象生成函數(shù)。 可暫停函數(shù), yield可暫停,next方法可啟動,每次返回的是yield后的表達(dá)式結(jié)果。 yield表達(dá)式本身沒有返回值,或者說總是返回undefined。next方法可以帶一個參數(shù),該參數(shù)就會被當(dāng)作上一個yield表達(dá)式的返回值。 我們先來看個例子: 可能結(jié)果跟你想象不一致,接下來我們逐行代碼分析: 首先 Generator 函數(shù)調(diào)用和普通函數(shù)不同,它會返回一個迭代器 當(dāng)執(zhí)行***次 next 時,傳參會被忽略,并且函數(shù)暫停在 yield (x + 1) 處,所以返回 5 + 1 = 6 當(dāng)執(zhí)行第二次 next 時,傳入的參數(shù)12就會被當(dāng)作上一個yield表達(dá)式的返回值,如果你不傳參,yield 永遠(yuǎn)返回 undefined。此時 let y = 2 12,所以第二個 yield 等于 2 12 / 3 = 8 當(dāng)執(zhí)行第三次 next 時,傳入的參數(shù)13就會被當(dāng)作上一個yield表達(dá)式的返回值,所以 z = 13, x = 5, y = 24,相加等于 42 我們再來看個例子:有三個本地文件,分別1.txt,2.txt和3.txt,內(nèi)容都只有一句話,下一個請求依賴上一個請求的結(jié)果,想通過Generator函數(shù)依次調(diào)用三個文件 從上例中我們看出手動迭代Generator 函數(shù)很麻煩,實現(xiàn)邏輯有點繞,而實際開發(fā)一般會配合 co 庫去使用。co是一個為Node.js和瀏覽器打造的基于生成器的流程控制工具,借助于Promise,你可以使用更加優(yōu)雅的方式編寫非阻塞代碼。 安裝co庫只需:npm install co 上面例子只需兩句話就可以輕松實現(xiàn) 我們可以通過 Generator 函數(shù)解決回調(diào)地獄的問題,可以把之前的回調(diào)地獄例子改寫為如下代碼: 七、async/await 1.Async/Await簡介 使用async/await,你可以輕松地達(dá)成之前使用生成器和co函數(shù)所做到的工作,它有如下特點: async/await是基于Promise實現(xiàn)的,它不能用于普通的回調(diào)函數(shù)。 async/await與Promise一樣,是非阻塞的。 async/await使得異步代碼看起來像同步代碼,這正是它的魔力所在。 一個函數(shù)如果加上 async ,那么該函數(shù)就會返回一個 Promise Generator函數(shù)依次調(diào)用三個文件那個例子用async/await寫法,只需幾句話便可實現(xiàn) 2.Async/Await并發(fā)請求 如果請求兩個文件,毫無關(guān)系,可以通過并發(fā)請求 到此,相信大家對“JS異步編程方案有哪些”有了更深的了解,不妨來實際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!ajax(url, () => { // 處理邏輯 })
ajax(url, () => { // 處理邏輯 ajax(url1, () => { // 處理邏輯 ajax(url2, () => { // 處理邏輯 }) }) })
f1.on('done', f2);
function f1() { setTimeout(function () { // ... f1.trigger('done'); }, 1000); }
jQuery.subscribe('done', f2);
function f1() { setTimeout(function () { // ... jQuery.publish('done'); }, 1000); }
jQuery.unsubscribe('done', f2);
let p = new Promise((resolve, reject) => { reject('reject') resolve('success')//無效代碼不會執(zhí)行 }) p.then( value => { console.log(value) }, reason => { console.log(reason)//reject } )
new Promise((resolve, reject) => { console.log('new Promise') resolve('success') }) console.log('end') // new Promise => end
// 例1 Promise.resolve(1) .then(res => { console.log(res) return 2 //包裝成 Promise.resolve(2) }) .catch(err => 3) .then(res => console.log(res))
// 例2 Promise.resolve(1) .then(x => x + 1) .then(x => { throw new Error('My Error') }) .catch(() => 1) .then(x => x + 1) .then(x => console.log(x)) //2 .catch(console.error)
// 例3 let fs = require('fs') function read(url) { return new Promise((resolve, reject) => { fs.readFile(url, 'utf8', (err, data) => { if (err) reject(err) resolve(data) }) }) } read('./name.txt') .then(function(data) { throw new Error() //then中出現(xiàn)異常,會走下一個then的失敗回調(diào) }) //由于下一個then沒有失敗回調(diào),就會繼續(xù)往下找,如果都沒有,就會被catch捕獲到 .then(function(data) { console.log('data') }) .then() .then(null, function(err) { console.log('then', err)// then error }) .catch(function(err) { console.log('error') })
ajax(url) .then(res => { console.log(res) return ajax(url1) }).then(res => { console.log(res) return ajax(url2) }).then(res => console.log(res))
function *foo(x) { let y = 2 * (yield (x + 1)) let z = yield (y / 3) return (x + y + z) } let it = foo(5) console.log(it.next()) // => {value: 6, done: false} console.log(it.next(12)) // => {value: 8, done: false} console.log(it.next(13)) // => {value: 42, done: true}
//1.txt文件 2.txt
//2.txt文件 3.txt
//3.txt文件 結(jié)束
let fs = require('fs') function read(file) { return new Promise(function(resolve, reject) { fs.readFile(file, 'utf8', function(err, data) { if (err) reject(err) resolve(data) }) }) } function* r() { let r1 = yield read('./1.txt') let r2 = yield read(r1) let r3 = yield read(r2) console.log(r1) console.log(r2) console.log(r3) } let it = r() let { value, done } = it.next() value.then(function(data) { // value是個promise console.log(data) //data=>2.txt let { value, done } = it.next(data) value.then(function(data) { console.log(data) //data=>3.txt let { value, done } = it.next(data) value.then(function(data) { console.log(data) //data=>結(jié)束 }) }) }) // 2.txt=>3.txt=>結(jié)束
function* r() { let r1 = yield read('./1.txt') let r2 = yield read(r1) let r3 = yield read(r2) console.log(r1) console.log(r2) console.log(r3) } let co = require('co') co(r()).then(function(data) { console.log(data) }) // 2.txt=>3.txt=>結(jié)束=>undefined
function *fetch() { yield ajax(url, () => {}) yield ajax(url1, () => {}) yield ajax(url2, () => {}) } let it = fetch() let result1 = it.next() let result2 = it.next() let result3 = it.next()
async function async1() { return "1" } console.log(async1()) // -> Promise {
let fs = require('fs') function read(file) { return new Promise(function(resolve, reject) { fs.readFile(file, 'utf8', function(err, data) { if (err) reject(err) resolve(data) }) }) } async function readResult(params) { try { let p1 = await read(params, 'utf8')//await后面跟的是一個Promise實例 let p2 = await read(p1, 'utf8') let p3 = await read(p2, 'utf8') console.log('p1', p1) console.log('p2', p2) console.log('p3', p3) return p3 } catch (error) { console.log(error) } } readResult('1.txt').then( // async函數(shù)返回的也是個promise data => { console.log(data) }, err => console.log(err) ) // p1 2.txt // p2 3.txt // p3 結(jié)束 // 結(jié)束
let fs = require('fs') function read(file) { return new Promise(function(resolve, reject) { fs.readFile(file, 'utf8', function(err, data) { if (err) reject(err) resolve(data) }) }) } function readAll() { read1() read2()//這個函數(shù)同步執(zhí)行 } async function read1() { let r = await read('1.txt','utf8') console.log(r) } async function read2() { let r = await read('2.txt','utf8') console.log(r) } readAll() // 2.txt 3.txt
分享文章:JS異步編程方案有哪些
網(wǎng)站鏈接:http://weahome.cn/article/ieppeh.html