JS異步解決方案的發(fā)展流程(一)

所謂"異步",簡(jiǎn)單說(shuō)就是一個(gè)任務(wù)分成兩段,先執(zhí)行第一段,然后轉(zhuǎn)而執(zhí)行其他任務(wù),等做好了準(zhǔn)備,再回過(guò)頭執(zhí)行第二段,比如,在我們燒水時(shí)可以干很多事情,當(dāng)水燒開(kāi)后在用水洗臉。這種不連續(xù)的執(zhí)行,就叫做異步。相應(yīng)地,連續(xù)的執(zhí)行,就叫做同步。例如在燒水的過(guò)程中我們一直等待水燒開(kāi)而不去干別的事情。

1.高階函數(shù)

函數(shù)作為一等公民,可以作為參數(shù)和返回值
高階函數(shù)至少滿足以兩個(gè)條件

  • 接受一個(gè)或多個(gè)函數(shù)作為輸入
  • 輸出一個(gè)函數(shù)

1.1 預(yù)置參數(shù)

let isType = function(type,obj){
    return Object.prototype.toString.call(obj) ===`[object ${type}]`
}
console.log(isType('Object',{}));
console.log(isType('Object',{}));
console.log(isType('String','hello'));
console.log(isType('String','hello'));
// 我們發(fā)現(xiàn)每次調(diào)用isType都需要傳入類型,所以我們可以先批量產(chǎn)出可供調(diào)用的函數(shù)
let isType = function(type){
    return function(obj){
        return Object.prototype.toString.call(obj) ===`[object ${type}]`
    }
}
let isObject = isType('Object');
let isString = isType('String');
console.log(isObject({}));
console.log(isObject({}));

1.2 預(yù)置函數(shù)

function after(times,cb){
    return function(){
        if(--times === 0){
            cb();
        }
    }
}
let eat = after(3,function(){console.log('吃完了')});
eat();
eat();
eat();
// 當(dāng)調(diào)用次數(shù)達(dá)到我們預(yù)置的次數(shù)時(shí),執(zhí)行我們預(yù)置的函數(shù)

到此我們對(duì)函數(shù)又有了進(jìn)一步的認(rèn)識(shí),下面我們就來(lái)開(kāi)始進(jìn)入異步的解決方案。

2.回調(diào)函數(shù)

所謂回調(diào)函數(shù),就是把任務(wù)的第二段單獨(dú)寫(xiě)在一個(gè)函數(shù)里面,等到重新執(zhí)行這個(gè)任務(wù)的時(shí)候,就直接調(diào)用這個(gè)函數(shù)。我們介紹一個(gè)常見(jiàn)的node中的異步方法readFile可以用來(lái)讀取文件。

fs.readFile(filename, function (err, data) {
  if (err) throw err;
  console.log(data);
});

在node中回調(diào)函數(shù)的第一個(gè)參數(shù)是錯(cuò)誤對(duì)象(error-first callbacks)

2.1 回調(diào)函數(shù)的應(yīng)用

function read(callback){
    setTimeout(function(){
        let result = 'zpfx';
        callback(result);
    })
}
read(function(data){
    console.log(data);
});

我們可以利用回調(diào)函數(shù)來(lái)解決異步問(wèn)題

3.回調(diào)的問(wèn)題

  • 異步不支持try/catch,回調(diào)函數(shù)是在下一事件環(huán)中取出,所以一般在回調(diào)函數(shù)的第一個(gè)參數(shù)預(yù)置錯(cuò)誤對(duì)象
  • 回調(diào)地獄問(wèn)題,異步多級(jí)依賴的情況下嵌套非常深,代碼難以閱讀的維護(hù)
  • 多個(gè)異步在某一時(shí)刻獲取所有異步的結(jié)果
  • 結(jié)果不能通過(guò)return返回

4.Promise

Promise本意是承諾,在程序中的意思就是承諾我過(guò)一段時(shí)間后會(huì)給你一個(gè)結(jié)果。 什么時(shí)候會(huì)用到過(guò)一段時(shí)間?答案是異步操作,異步是指可能比較長(zhǎng)時(shí)間才有結(jié)果的才做,例如網(wǎng)絡(luò)請(qǐng)求、讀取本地文件等

4.1 Promise的三種狀態(tài)

例如媳婦說(shuō)想買個(gè)包,這時(shí)候他就要"等待"我的回復(fù),我可以過(guò)兩天買,如果買了表示"成功",如果我
最后拒絕表示"失敗",當(dāng)然我也有可能一直拖一輩子

  • Pending Promise對(duì)象實(shí)例創(chuàng)建時(shí)候的初始狀態(tài)
  • Fulfilled 可以理解為成功的狀態(tài)
  • Rejected 可以理解為失敗的狀態(tài)

then 方法就是用來(lái)指定Promise 對(duì)象的狀態(tài)改變時(shí)確定執(zhí)行的操作,resolve 時(shí)執(zhí)行第一個(gè)函數(shù)
(onFulfilled),reject 時(shí)執(zhí)行第二個(gè)函數(shù)(onRejected)

4.2 構(gòu)造Promise

promise的方法會(huì)立刻執(zhí)行

let promise = new Promise(() => {
    console.log('hello');
}); 
console.log('world'); 
// hello
// world

4.3 promise也可以代表一個(gè)未來(lái)的值

const fs = require('fs');
let  promise = new Promise((resolve, reject) => {
    fs.readFile('./content.txt', 'utf8', function (err, data)  {
        if (err) return reject(err); 
        resolve(data);
    })
 }); 
promise.then(data => {
    console.log(data);  
});
promise.then(data => {
    console.log(data);
});
// 一個(gè)promise實(shí)例可以多次調(diào)用then當(dāng)成功后會(huì)將結(jié)果依次執(zhí)行

4.4 代表一個(gè)用于不會(huì)返回的值

const fs = require('fs');
let  promise = new Promise((resolve, reject) => { });
promise.then(data => {
    console.log(data); //代表一個(gè)用于不會(huì)返回的值
}); 

4.5 實(shí)現(xiàn)買包的案例

function buyPack()  {
    return new Promise((resolve, reject) => {
        setTimeout(function  ()  {
            var  random = Math.random();
            if (random > 0.5) {
                resolve('買');
            } else {
                resolve('不買');
            }
        }, 2000)
    })
}
buyPack().then(data => {
    console.log(data);
}, data => {
    console.log(data);
}); 

4.6 Error會(huì)導(dǎo)致觸發(fā)Reject

可以采用then的第二個(gè)參數(shù)捕獲失敗,也可以通過(guò)catch函數(shù)進(jìn)行

function buyPack()  {
    return new Promise((resolve, reject) => {
        throw new Error('沒(méi)錢')
    })
}
buyPack().then(data => {
    console.log(data);
}, data => {
    console.log(data);
}); 

5.解決回調(diào)地獄

回歸正題,先用promise解決第一個(gè)問(wèn)題"回調(diào)地獄"

// 1.txt => 2.txt
// 2.txt => 我很帥
let fs = require('fs');
function read(){
    fs.readFile('./1.txt','utf8',function(err,data){
        if(err) return console.log(err);
        fs.readFile(data,'utf8',function(err,data){
            if(err) return console.log(err);
            console.log(data); // 我很帥
        })
    })
}
read();

改寫(xiě)Promise形式

let fs = require('fs');
function read(file){
    return new Promise(function(resolve,reject){
        fs.readFile(file,'utf8',function(err,data){
            if(err) return reject(err);
            resolve(data);
        })
    })
}
read('./1.txt').then(function(data){
    return read(data);
}).then(function(data){
    console.log(data)
}).catch(function(err){
    console.log(err)
});

當(dāng)?shù)谝粋€(gè)then中返回一個(gè)promise,會(huì)將返回的promise的結(jié)果,傳遞到下一個(gè)then中。這就是比較著名的鏈?zhǔn)秸{(diào)用了。

6.同步異步的結(jié)果

我們將多個(gè)異步請(qǐng)求的結(jié)果在同一時(shí)間進(jìn)行匯總

// 1.txt => template
// 2.txt => data
let fs = require('fs');
let result = {}
function out(key,data) {
    result[key] = data;
    if(Object.keys(result).length === 2){
        console.log(result)
    }
}
fs.readFile('./1.txt', 'utf8', function (err, data) {
    if (err) return console.log(err);
    out('template',data);
})
fs.readFile('./2.txt', 'utf8', function (err, data) {
    if (err) return console.log(err);
    out('data',data);
});
// 這種方式并不好,要聲明全局的對(duì)象,并且成功的數(shù)量也是寫(xiě)死的,我們可以使用偏函數(shù)來(lái)進(jìn)行改寫(xiě)
let fs = require('fs');
let result = {}
function after(times,cb) {
    let result = {}
    return function(key,data){
        result[key] = data;
        if(Object.keys(result).length === times){
            cb(result)
        }
    }
}
let out = after(2,function(data){
    console.log(data)
})
fs.readFile('./1.txt', 'utf8', function (err, data) {
    if (err) return console.log(err);
    out('template',data);
})
fs.readFile('./2.txt', 'utf8', function (err, data) {
    if (err) return console.log(err);
    out('data',data);
});

最后我們可以采用Promise.all方法來(lái)進(jìn)行簡(jiǎn)化

let fs = require('fs');
function read(file){
    return new Promise(function(resolve,reject){
        fs.readFile(file,'utf8',function(err,data){
            if(err) return reject(err);
            resolve(data);
        })
    })
}
Promise.all([read('1.txt'),read('2.txt')]).then(([template,data])=>{
    console.log({template,data})
});
// 不管兩個(gè)promise誰(shuí)先完成,Promise.all 方法會(huì)按照數(shù)組里面的順序?qū)⒔Y(jié)果返回

7.Promise.race

接受一個(gè)數(shù)組,數(shù)組內(nèi)都是Promise實(shí)例,返回一個(gè)Promise實(shí)例,這個(gè)Promise實(shí)例的狀態(tài)轉(zhuǎn)移取決于參數(shù)的Promise實(shí)例的狀態(tài)變化。當(dāng)參數(shù)中任何一個(gè)實(shí)例處于resolve狀態(tài)時(shí),返回的Promise實(shí)例會(huì)變?yōu)閞esolve狀態(tài)。如果參數(shù)中任意一個(gè)實(shí)例處于reject狀態(tài),返回的Promise實(shí)例變?yōu)閞eject狀態(tài)。

Promise.race([read('1.txt'),read('2.txt')]).then(data=>{
    console.log({template,data})
},(err)=>{
    console.log(err)
});

8.Promise.resolve

返回一個(gè)Promise實(shí)例,這個(gè)實(shí)例處于resolve狀態(tài)

Promise.resolve('成功').then(data=>{ 
    console.log(data);
});

9.Promise.reject

返回一個(gè)Promise實(shí)例,這個(gè)實(shí)例處于reject狀態(tài)

Promise.reject('失敗').then(data=>{ 
   console.log(data); 
},err=>{ 
console.log(err); 
}) 

下一節(jié)我們會(huì)繼續(xù)介紹generator和async/await的用法,以及Promise的實(shí)現(xiàn)原理,喜歡的點(diǎn)個(gè)贊吧_!
支持我的可以給我打賞

打賞

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Promise 對(duì)象 Promise 的含義 Promise 是異步編程的一種解決方案,比傳統(tǒng)的解決方案——回調(diào)函...
    neromous閱讀 8,823評(píng)論 1 56
  • 你不知道JS:異步 第三章:Promises 在第二章,我們指出了采用回調(diào)來(lái)表達(dá)異步和管理并發(fā)時(shí)的兩種主要不足:缺...
    purple_force閱讀 2,226評(píng)論 0 4
  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持,譯者再次奉上一點(diǎn)點(diǎn)福利:阿里云產(chǎn)品券,享受所有官網(wǎng)優(yōu)惠,并抽取幸運(yùn)大...
    HetfieldJoe閱讀 11,115評(píng)論 26 95
  • 你不知道JS:異步 第三章:Promises 接上篇3-1 錯(cuò)誤處理(Error Handling) 在異步編程中...
    purple_force閱讀 1,488評(píng)論 0 2
  • 弄懂js異步 講異步之前,我們必須掌握一個(gè)基礎(chǔ)知識(shí)-event-loop。 我們知道JavaScript的一大特點(diǎn)...
    DCbryant閱讀 2,869評(píng)論 0 5

友情鏈接更多精彩內(nèi)容