异步要领的生长流程

@(同步与异步)[callback|Promise|Generator + Co|Async + Await]

  • 回调函数
  • Promise
  • Generator + Co
  • Async/Await

同步要领

同步递次且一连实行,必需实行终了或返回后才会继承实行后续代码。

函数(封装,私有化)

  • 是由事宜驱动的或许当它被挪用时实行的可重复运用的代码块

高阶函数

  • 吸收函数作为输入:函数能够看成参数通报给另一个函数
  • 输出一个函数:一个函数实行后返回一个函数

例:
推断内容数据范例是不是为希冀范例值

/**
 * 推断内容值是不是为希冀的范例
 * @param {*} content 内容值
 * @param {String} expect 希冀值的范例
 * @returns {Boolean} 返回内容值是不是为希冀的范例
 */
function isType(content, expect) {
    // 四种推断范例要领: constructor、typeof、instanceof、Object.prototype.toString
    let type = Object.prototype.toString.call(content).replace(/\[object\s|\]/g, '');
    return type === expect;
}

console.log(isType('hello', 'String'));    // true

扩大:运用高阶函数来定义推断各数据范例的要领

/**
 * 返回用于推断内容值是不是为希冀的范例的函数
 * @param {String} expect 希冀范例
 * @returns {function(*)} 返回推断函数
 */
function isType(expect) {
    return function (content) {
        let type = Object.prototype.toString.call(content).replace(/\[object\s|\]/g, '');
        return type === expect;
    }
}

let util = {};
let types = ['Object', 'Array', 'Function', 'String', 'Number', 'Boolean', 'Null', 'Undefined'];

types.forEach((item) => {
    util[`is${item}`] = isType(item);
});

console.log(util.isNumber(86));  // true
console.log(util.isBoolean('true'));  // false

异步要领

异步示意非一连实行,如:setTimeoutAjax、事宜等要领,一般会在另一个线程中实行,不会壅塞主线程。

异步编程的要领,大概有下面四种。

  • 回调函数
  • 事宜监听
  • 宣布/定阅
  • Promise 对象

异步要领假如出错了不能捕获
try/catch 毛病

猎取的效果不能通过
return 返回

回调函数

阮一峰:所谓回调函数,就是把使命的第二段零丁写在一个函数内里,比及从新实行这个使命的时刻,就直接挪用这个函数。它的英语名字 callback,直译过来就是”从新挪用”。(原文)

虽然回调函数多用于异步编程,但带有回调函数的要领不一定是异步的。

题目1:
第二个要求是依赖于第一个要求
例:

// error-first
// 回调要领实行之前抛出的毛病,顺序没法捕获,只能看成参数传入回调要领
fs.readFile('./1.txt', 'utf8', function (err, a) {
    fs.readFile('./2.txt', 'utf8', function (err, b) {
        console.log(a, b);
    });
});

题目2:
两个异步要求,同时拿到两个异步要求的效果
例:

function after(times, callback) {
    let arr = [];

    return function (data) {
        arr.push(data);
        if(--times === 0){
            callback(arr);
        }
    };
}

let out = after(2, function (data) { 
    console.log(data);
});

out('once');
out('twice');

第二次实行时会一同获得效果 ['once', 'twice']
能够理解为回调要领相当于两个异步要求有关联。

回调函数可能会涌现会涌现多重嵌套,从而形成代码难以治理,提高了分外的保护本钱,这类状况被称为 发作回调地狱 (Callback Hell)

宣布/定阅形式

  • 宣布(这件事发作时 我要顺次实行)
  • 定阅(我预先想到的事)
let fs = require('fs');
let events = {
    cbs: [],
    res: [],
    on(callback) {
        this.cbs.push(callback);
    },
    emit(data) {
        this.res.push(data);
        this.cbs.forEach(item => item(this.res));
    }
};

// 定阅历程  
events.on((data) => {
    if (data.length === 2) {
        console.log(data);
    }
});

// 定阅历程  
events.on(() => {
    console.log('good');
});

fs.readFile('file-01.txt', 'utf8', (err, data) => {
    events.emit(data);
});

fs.readFile('file-02.txt', 'utf8', (err, data) => {
    events.emit(data);
});

宣布定阅同时拿到两个异步效果,须要回调函数,Promise不须要回调函数

Promise

promise 对象用于示意一个异步操纵的终究状况(完成或失利),以及其返回的值。
三个状况 胜利态 失利态 守候态
一个 Promise有以下几种状况:

  • pending: 初始守候状况,既不是胜利,也不是失利状况。
  • fulfilled: 胜利态,意味着操纵胜利完成。
  • rejected: 失利态,意味着操纵失利。

默许状况是守候态
守候态能够变成 胜利态或失利态
胜利就不能失利
也不能从失利变成胜利
不支持的低版本浏览器 须要 es6-promise 模块

Promise

new Promise 时会通报一个实行器 executor
executor 实行器是马上实行的,而且吸收两个函数resolvereject 作为其参数
每一个 promise 实例都有一个 then 要领,参数是胜利和失利,胜利会有胜利的值 失利
同一个 promise 能够屡次 then

let promise = new Promise((resolve, reject) => {
    reject('买');
});

promise
    .then((data) => {
        console.log('data', data);
    }, (err) => {
        console.log('err', err);
    });

promise
    .then((data) => {
        console.log('data', data);
    }, (err) => {
        console.log('err', err);
    });

回调地狱

Promise 处理回调地狱
例:

let fs = require('fs');
function read(path, encoding) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, encoding, function (err, data) {
            if (err) return reject(err);
            resolve(data);
        });
    });
}

屡次 then

同一个 promise 能够屡次 then
胜利的回调或许失利的回调实行后能够返回一个 promise
会将这个 promise 的实行效果通报给下一次 then
假如返回一个一般的值 ,会将这个一般值通报倒下一次 then 的胜利的参数

read('./file-01.txt', 'utf8')
    .then(data => {
    
        // 由于 read 要领会返回一个 promise ,返回read实行相当于返回一个 promise
        // 会将这个 promise 的实行胜利的效果通报给下一次 then 的 resolve 
        return read(data, 'utf8');
    }, err => {
        console.log(err);
    })
    .then(data => {
    
        // 假如返回一个一般的值 ,会将这个一般值通报倒下一次 then 的胜利的参数
        return [data];
    }, err => {
        console.log(err);
    })
    .then(data => {
    
        // 返回的是一个一般值
        console.log(data);
        
        // 没有写 return ,相当于返回一个 undefined ,下一个then的胜利值则为 undefined
    }, err => {
        console.log(err);
    })
    .then(data => {
    
        // 上一个 then 没有返回值,默许值为 undefined
        // undefined 也算胜利
        console.log(data);
        
        // 抛出毛病,将传给下一个 then 的 reject 
        throw new Error('xxx');
    }, err => {
        console.log(err);
    })
    .then(null, err => {
    
        // 假如上一个 then 抛出毛病,近来的 reject 会实行
        // reject 实行后默许下一个 then 会吸收 undefined 
        console.log(err);
    })
    .then(data => {
    
        // 上一个 then 中失利没有返回值,默许为 undefined 
        console.log('resolve');        
    }, err => {
        console.log('reject');
    });

替代 reject

能够将一切 reject 要领都用 catch 替代

read('./file-01.txt', 'utf8')
    .then(data => {
        return read(data, 'utf8');
    })
    .then(data => {
        return [data];
    })
    .then(data => {
        console.log(data);
    })
    .then(data => {
        console.log(data);
        
        // 抛出毛病后,找到近来的吸收毛病要领
        // 假如一切的 then 都没有 reject 要领,则找末了一个 catch
        throw new Error('xxx');
    })
    .then(null)
    .then(data => {
        console.log('resolve');        
    })
    .catch(err => {
        console.log(err);
    });

then穿透

let promise = new Promise((resolve, reject) => {
    resolve('ok);
});

// 胜利不写的时刻,默许: value => value
// 失利不写的时刻,默许: err => {throw err}
promise
    .then()
    .then(data => {
        
    });

all

处理多个异步要求题目
例:
法守候两个 promise 都实行完成后,会返回一个新的 promise
假如有一个失利就失利了

Promise
    .all([read('file-01.txt', 'utf8'), read('file-02.txt', 'utf8')])
    .then(data => {
        console.log(data);
    }, err => {
        console.log(err);
    });

race

race 一样返回一个 Promise 其胜利与失利的状况取决于最早返回效果的状况
谁跑的快就用谁的效果

Promise
    .race([read('file-01.txt', 'utf8'), read('file-02.txt', 'utf8')])
    .then(data => {
        console.log(data);
    }, err => {
        console.log(err);
    });

有状况的 promise

建立一个一出生就胜利或许失利的 promise

// 胜利态
Promise
    .resolve('123')
    .then(data => {
        console.log(data);
    });
    
Promise
    .reject('123')
    .then(null, err => {
        console.log(err)
    });

链式挪用返回 this
promise 不能返回 this
promise 完成链式挪用是靠返回一个新的 promise

Callback 到了 Promise 时期,不须要再去挪用回调函数。
Promise 的典范运用 Axios Fetch

但实际上 Promise 只是对回调函数的革新的写法罢了,并非新的语法功用,本来的语义也被一堆 then 损坏掉了。

Generator迭代器(迭代形式)

es6 完成的 generator (天生器)会返回一个内部指针(迭代器)。

例:

for (let i of [1, 2, 3]) {
    console.log(i);
}

例:

function arg() {
    // arguments是一个类数组
    // 有索引,有长度,但不是数组
    // 没有数组的要领
    // 但能够被迭代
    // 内置iterator
    for (let i of arguments) {
        console.log(i);
    }
}

arg(1, 2, 3, 4);

例:

let arr = {0: 1, 1: 2, 2: 3, length: 3};

for (let i of arr) {
    console.log(i);
}

// TypeError: arr is not iterable
// arr是不可被迭代的,由于arr没有迭代器

为自定义类数组增添迭代要领,迭代要领是帮我们迭代

* 代表一个天生器,function 关键字和要领名之间有个 * ,这就代表一个 generator

天生器天生一个迭代器,合营 yield,碰到 yield 就停息,

迭代器必需返回一个对象,对象里有一个 next 要领,挪用迭代器的 next 要领挪动内部指针,每挪用一次 next 要领就能够返回一个对象 {value,done},对象里有俩属性:示意当前阶段的信息 ,valueyield 表达式的值,done 是一个示意是不是实行终了的布尔值。

再次挪用 next 继承实行。
碰到 return 时就迭代完成了,没写 return 则为 return undefined,也算完成了

function * thing() {
    // 天生器返回一个迭代器
    // yield产出,而且为未完成状况
    yield 1;

    // return示意完成
    return 2;
}

// 返回一个迭代器
let it = thing();

迭代器里有 next 要领,返回一个对象,包括 donevalue

// { value: 1, done: false }
console.log(it.next());

// { value: 2, done: true }
console.log(it.next());

传值

function * some() {

    let a = yield 1;
    console.log('a', a);
    let b = yield 2;
    console.log('b', b);

    return b;
}

let so = some();

// 第一个next通报参数无效,没有意义
// 传了100也没用
console.log(so.next('100'));     // { value: 1, done: false }

// 第二个next传参是第一次yield的返回值
console.log(so.next('200'));    // a 200
                                // { value: 2, done: false }

console.log(so.next('300'));    // b 300
                                // { value: '300', done: true }

例:
猎取 1.txt 内容:2.txt 2.txt内容是终究效果

let blueBird = require('bluebird');
let fs = require('fs');

let read = blueBird.promisify(fs.readFile);

function* readFile() {
    let data1 = yield read('1.txt', 'utf8');
    let data2 = yield read(data1, 'utf8');
    return data2;
}

合营 promise 运用:

let it = readFile();

it.next().value
    .then(data => {
        return it.next(data).value;
    })
    .then(data => {
        console.log(data);
    });

合营 co 运用:

function co(it) {
    return new Promise((resolve, reject) => {
        function next(dataset) {
            let {value, done} = it.next(dataset);
            if (!done) {
                value.then(data => {
                    next(data);
                }, reject);
            } else {
                resolve(value);
            }
        }
        next();
    });
}

co(readFile())
    .then(data => {
        console.log(data);
    });

运用 co

let co = require('co');

co(readFile())
    .then(data => {
        console.log(data);
    });

async / await

async await 实际上是一个语法糖
async + await = generator + co

let blueBird = require('bluebird');
let fs = require('fs');

let read = blueBird.promisify(fs.readFile);

async function readFile() {
    let data1 = await read('1.txt', 'utf8');
    let data2 = await read(data1, 'utf8');
    return data2;
}

readFile()
    .then(data => {
        console.log(data);
    });

    原文作者:蛋炒太阳
    原文地址: https://segmentfault.com/a/1190000015589778
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞