@(同步与异步)[callback|Promise|Generator + Co|Async + Await]javascript
同步顺序且连续执行,必须执行完毕或返回后才会继续执行后续代码。html
例:
判断内容数据类型是否为指望类型值java
/** * 判断内容值是否为指望的类型 * @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
扩展:利用高阶函数来定义判断各数据类型的方法ios
/** * 返回用于判断内容值是否为指望的类型的函数 * @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
异步表示非连续执行,如:setTimeout
、Ajax
、事件等方法,一般会在另外一个线程中执行,不会阻塞主线程。 es6
异步编程的方法,大概有下面四种。编程
异步方法若是出错了不能捕获try/catch
错误
获取的结果不能经过return
返回
阮一峰:所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到从新执行这个任务的时候,就直接调用这个函数。它的英语名字 callback,直译过来就是"从新调用"。(原文)数组
虽然回调函数多用于异步编程,但带有回调函数的方法不必定是异步的。promise
问题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
有如下几种状态:
默认状态是等待态
等待态能够变成 成功态或失败态
成功就不能失败
也不能从失败变成成功
不支持的低版本浏览器 须要 es6-promise
模块
Promise
类new Promise
时会传递一个执行器 executor
executor
执行器是当即执行的,而且接受两个函数resolve
和 reject
做为其参数
每一个 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
破坏掉了。
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}
,对象里有俩属性:表示当前阶段的信息 ,value
为 yield
表达式的值,done
是一个表示是否执行完毕的布尔值。
再次调用 next
继续执行。
遇到 return
时就迭代完成了,没写 return
则为 return undefined
,也算完成了
function * thing() { // 生成器返回一个迭代器 // yield产出,而且为未完成状态 yield 1; // return表示完成 return 2; } // 返回一个迭代器 let it = thing();
迭代器里有 next
方法,返回一个对象,包含 done
和 value
// { 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
= 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); });
完