什麼是非同步/同步呼叫
非同步呼叫就是你 喊 你朋友吃飯 ,你朋友說知道了 ,待會忙完去找你 ,你就去做別的了。
同步呼叫就是你 喊 你朋友吃飯 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你們一起去。
非同步不支援try/catch,try/catch只針對同步程式碼
- Callback
callback就是在回撥函式,在函式中會作為引數來實現:
let fs = require('fs'); // readFile
fs.readFile('./2.promise/1.txt','utf8',function(err,data){ // error-first
fs.readFile(data,'utf8',function(err,data){ // error-first
console.log(data);
});
});
// 並行 無法在同一時刻合併兩節非同步的結果,非同步方案不支援return
fs.readFile('./2.promise/1.txt','utf8',function(err,data){ // error-first
});
fs.readFile('./2.promise/2.txt','utf8',function(err,data){ // error-first
console.log(data);
});複製程式碼
高階函式
函式可以作為引數或者函式還可以作為返回值
1)批量生成函式
function isType(type){ // 偏函式
return function(content){
return Object.prototype.toString.call(content) === `[object ${type}]`;
}
}
let isString = isType('String');
let isArray = isType('Array');
console.log(isArray('hello'));複製程式碼
2)預置函式作為引數 loadsh _.after
function after(times,callback){
return function(){
if(--times === 0){
callback();
}
}
}
let eat = after(3,function(){
console.log('飽了')
})
eat();
eat();
eat();複製程式碼
2. Promise
callback雖然也能解決問題,但是要是多個函式巢狀的話不僅不好看難以維護,還會形成回撥地獄。這個時候promise就出來啦!
let promise = new Promise(function (resolve,reject) {
resolve(100)
})
//鏈式呼叫的特點,將第一次成功的返回值當成下一次回撥函式的引數
let p2 = promise.then(function (data) {
return new Promise(function (resolve,reject) {
resolve(data)
})
},function () {
})
p2.then().then().then(function (data) {
console.log(data);
},function(err){
console.log(err)
})複製程式碼
promise 解決了回撥地獄的問題,還解決了同步非同步的返回結果,按照順序執行
3. generator + co(Promise)
co 是由tj 寫的一個自動迭代的庫。generator函式要用*來標識,yield(暫停,產出),它將函式分割成很多塊,用next來往下執行,返回結果是一個迭代器,yield後面跟著的是value值,yield等號前面的是我們當前呼叫next傳進來的,但是第一次傳進來是無效的:
function* read() {
console.log(1);
let a = yield '珠峰';
console.log(a);
let b = yield 9
console.log(b);
return b;
}
let it = read();
console.log(it.next('213')); // {value:'珠峰',done:false}
console.log(it.next('100')); // {value:9,done:false}
console.log(it.next('200')); // {value:200,done:true}
console.log(it.next('200')); // {value:200,done:true}複製程式碼
看co的原始碼就會發現co()執行返回的是一個new Promise:
// 非同步 generator主要和promise搭配使用
let bluebird = require('bluebird');
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);//promisify可以轉化成promise
function* r() {
let content1 = yield read('./2.promise/1.txt', 'utf8');
let content2 = yield read(content1, 'utf8');
return content2;
}
// co庫 npm install co 可以自動的將generator進行迭代
// let co = require('co');
function co(it) {
return new Promise(function (resolve, reject) {
function next(d) {
let { value, done } = it.next(d);
if (!done) {
value.then(function (data) { // 2,txt
next(data)
}, reject)
} else {
resolve(value);
}
}
next();
});
}
co(r()).then(function (data) {
console.log(data)
})
// generator原理是將一個函式劃分成若干個小函式,沒次呼叫時移動指標,內部是一個條件判斷,走對應的邏輯
// it.next().value.then(function(data){ // 2.txt
// it.next(data).value.then(function(data){
// console.log(it.next(data).value);
// });
// })複製程式碼
這裡有一個外掛就是bluebird,它有個promisify的方法,是可以將引數promise化。另外他的promisifyAll方法是將一個方法下面的函式轉化成promise。:
let fs = require('fs');
let bluebird = require('bluebird');
function promisify(fn) { // promise化 將回撥函式在內部進行處理
return function (...args) {
return new Promise(function (resolve, reject) {
fn(...args, function (err, data) {
if (err) reject(err);
resolve(data);
})
})
}
}
function promisifyAll(obj) {
Object.keys(obj).forEach(key => { // es5將物件轉化成陣列的方法
if (typeof obj[key] === 'function') {
obj[key + 'Async'] = promisify(obj[key])
}
})
}
promisifyAll(fs); // 將所有的方法全部增加一個promise化
fs.readFileAsync('./2.promise/1.txt', 'utf8').then(function (data) {
console.log(data);
});複製程式碼
4.async await
號稱非同步呼叫的終極解決方案,成功解決了回撥地獄,併發執行非同步,在同一時刻同步返回結果,Promise.all,解決返回值問題,可以實現try,catch.
let bluebird = require('bluebird');
let fs = require('fs');
let read = bluebird.promisify(fs.readFile);
// 用async 來修飾函式,aysnc需要配await,await只能promise
// async和await(語法糖) === co + generator
async function r(){
try{
let content1 = await read('./2.promise/100.txt','utf8');
let content2 = await read(content1,'utf8');
return content2;
}catch(e){ // 如果出錯會catch
console.log('err',e)
}
}
// async函式返回的是promise,
r().then(function(data){
console.log('flag',data);
},function(err){
})複製程式碼