所謂"異步",簡(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è)贊吧_!
支持我的可以給我打賞哈
