淺談非同步呼叫幾種方式

好想吃涼皮發表於2018-04-06

什麼是非同步/同步呼叫

非同步呼叫就是你 喊 你朋友吃飯 ,你朋友說知道了 ,待會忙完去找你 ,你就去做別的了。
同步呼叫就是你 喊 你朋友吃飯 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你們一起去。

非同步不支援try/catch,try/catch只針對同步程式碼

  1. 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){

})複製程式碼




相關文章