需求:有三個api,已知第一個api的地址 "1",從第一個api裡獲取第二個api的地址,從第二個api裡獲取第三個api的地址。 用fs模組模擬請求api。
api的資料結構
{
"msg":"This is x", //message
"data":{
"api":"x" //api的地址
}
}
複製程式碼
回撥函式
- 傳統方式,符合傳統js程式設計
- 可讀性差
- 容易形成回撥地獄
let fs = require('fs');
function getApi(api, callBack) {
fs.readFile(`./${api}.json`, "utf8", (err, data) => {
if (err) {
console.log(err);
} else {
const api = JSON.parse(data);
callBack(api);
}
});
}
getApi('1',(api)=>{
console.log(api.msg);
getApi(api.data.api,(api)=>{
console.log(api.msg);
getApi(api.data.api,(api)=>{
console.log(api.msg);
})
})
});
複製程式碼
事件訂閱
- 釋出訂閱模式,每次請求訂閱事件,得到資料後,釋出事件
- 如果請求api很多,要訂閱很多事件
- 程式碼冗餘,不方便維護
let fs = require('fs');
function getApi(api, callBack) {
fs.readFile(`./${api}.json`, "utf8", (err, data) => {
if (err) {
console.log(err);
} else {
const api = JSON.parse(data);
callBack(api);
}
});
}
function Event() {
this.event = {};
}
Event.prototype.on = function (type,callBack) {
if(this.event[type]){
this.event[type].push(callBack);
}else{
this.event[type] = [callBack];
}
};
Event.prototype.emit = function (type,...data) {
this.event[type].forEach((item)=>item(...data));
};
let event = new Event();
event.on('api1',function () {
getApi(1, function (api) {
console.log(api.msg);
event.emit('api2',api.data.api);
});
});
event.on('api2',function (api) {
getApi(api, function (api) {
console.log(api.msg);
event.emit('api3',api.data.api);
});
});
event.on('api3',function (api) {
getApi(api, function (api) {
console.log(api.msg);
});
});
event.emit('api1');
複製程式碼
Promise
- 鏈式呼叫,程式碼易讀
let fs = require('fs');
//Promise
function getApi(api) {
return new Promise(function (resolve,reject) {
fs.readFile(`./${api}.json`, "utf8", (err, data) => {
if (err) {
reject(err);
} else {
const api = JSON.parse(data);
resolve(api);
}
});
});
}
getApi(1).then(function (data) {
console.log(data.msg);
return getApi(data.data.api);
}).then(function (data) {
console.log(data.msg);
return getApi(data.data.api);
}).then(function (data) {
console.log(data.msg);
});
複製程式碼
Generator
- 執行next(),返回物件,key分別是value,done。value是yield 語句後面的內容,done表示是否還有next可以執行
- 以 let api2 = yield getApi(api1) 為例api2是next傳入的引數。
- 個人感覺不如promise好用。。。(歡迎來噴,交流學習)
let fs = require('fs');
//generator
function getApi(api) {
return new Promise(function (resolve,reject) {
fs.readFile(`./${api}.json`, "utf8", (err, data) => {
if (err) {
reject(err);
} else {
const api = JSON.parse(data);
resolve(api);
}
});
});
}
function* gen(api1) {
//api1 api2 ap3 是接收使用者輸入的
let api2 = yield getApi(api1);
let api3 = yield getApi(api2);
yield getApi(api3);
}
let g = gen(1);
let api1 = g.next();
api1.value.then(function (data) {
console.log(data.msg);
return data
}).then(function (data) {
let api2 = g.next(data.data.api).value;
return api2;
}).then(function (data) {
console.log(data.msg);
let api3 = g.next(data.data.api).value;
return api3;
}).then(function (data) {
console.log(data.msg);
});
複製程式碼
async
- Generator的語法糖,比Generator易讀、容易理解
- 程式碼同步寫,同步執行
let fs = require('fs');
// async await
function getApi(api) {
return new Promise(function (resolve, reject) {
fs.readFile(`./${api}.json`, "utf8", (err, data) => {
if (err) {
reject(err);
} else {
const api = JSON.parse(data);
resolve(api);
}
});
});
}
async function executor(api) {
let api1, api2, api3;
await getApi(api).then(function (data) {
api1 = data;
console.log(api1.msg);
});
await getApi(api1.data.api).then(function (data) {
console.log(data.msg);
api2 = data;
});
await (function () {
if (api2.data) {
getApi(api2.data.api).then(function (data) {
console.log(data.msg);
});
}
})();
}
executor(1);
複製程式碼
小結
小結不是小姐。個人理解,async是趨勢,Promise比Generator好用。媽媽再也不會擔心回撥地獄啦