let fs = require('fs')
fs.readFile('./name.txt','utf-8',(err,data)=>{
console.log(data);
if(data){
fs.readFile(data,'utf-8',(err,data)=>{
console.log(data)
if(data){
fs.readFile(data,'utf-8',(err,data)=>{
console.log(data)
})
}
})
}
})
就这样层层嵌套,人称之为回调地狱,回调函数不好管理,代码阅读性非常差
在使用JavaScript时,为了实现某些逻辑经常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种情况也被成为回调地狱。
try catch 只能捕获同步异常,不能捕获异步异常
try {
console.log(a)
} catch (error) {
console.log('同步',error)
}
try {
setTimeout(() => {
console.log(a)
}, 30);
} catch (error) {
console.log('异步',error)
}
解决回调地狱
Promise对象
let fs = require('fs')
/***
* nodejs中很多函数都需要promise化
*/
function readFile(path){
return new Promise((resolve,reject)=>{
fs.readFile(path,'utf-8',(err,data)=>{
if(data){
resolve(data)
}
})
})
}
readFile('./name.txt').then((data)=>{
return readFile(data)
}).then(data=>{
return readFile(data)
}).then(data=>{
console.log(data)
})
Generator函数
async函数
promise
promise A+ -> ES6
Promise 构造器Promise 构造器主要用于包装不支持promise(返回值不是Promise)的函数。
new Promise(executor) executor这是一个双参函数,参数为resolve和reject。
Promise的实现会立即执行executor,并传入resolve和reject函数
(Promise构造器将会在返回新对象之前executor)。
当resolve和reject函数被调用时,它们分别对promise执行resolve和reject。
executor通常会触发一些异步运算,一旦运算成功完成,则resolve掉这个promise,如果出错则reject掉。
如果executor函数执行时抛出异常,promise状态会变为rejected。executor的返回值也会被忽略。
const promise1 = new Promise((resolve, reject) => {
console.log('promise1', resolve, reject)
});
console.log(2)
// promise1 ƒ () { [native code] } ƒ () { [native code] }
// 2
// 同步执行
我们通过new关键字和Promise构造器创建它的对象。
这个构造器接受一个名为"executor function"的函数。
这个函数应当接受两个函数参数。
当异步任务成功时,第一个函数(resolve)将被调用,并返回一个值代表成功。
当其失败时,第二个函数(reject)将被调用,并返回失败原因(失败原因通常是一个error对象)。
let promise = new Promise(function(resolve,reject){
setTimeout(() => {
Math.random()*100>60?resolve('ok'):reject('no')
}, 30);
})
promise.then(
successVal=>{
console.log(successVal) //ok
},
failValue=>{
console.log(failValue) //no
}
)
执行顺序
let promise = new Promise(function(resolve,reject){
console.log(0)
resolve(1)
})
promise.then(
successVal=>{
console.log(successVal) //ok
},
failValue=>{
console.log(failValue) //no
}
)
console.log(2)
// 0 2 1
链式调用和返回值
直接return值
let promise = new Promise(function(resolve,reject){
setTimeout(() => {
Math.random()*100>60?resolve('ok'):reject('no')
}, 30);
})
promise.then(
successVal=>{
console.log(successVal) //ok
return 11 //第二次链式调用的值拿到的是第一次return的值
},
failValue=>{
console.log(failValue) //no
return 22
}
).then(
successVal=>{
console.log('then2-',successVal) //ok
},
failValue=>{
console.log('then2-',failValue) //no
}
)
// ok then2- 11
// no then2- 22
return一个Promise
let promise = new Promise(function(resolve,reject){
setTimeout(() => {
Math.random()*100>60?resolve('ok'):reject('no')
}, 30);
})
promise.then(
successVal=>{
console.log(successVal) //ok
return new Promise((res,rej)=>{
res('newP2')
})
},
failValue=>{
console.log(failValue) //no
return 22
}
).then(
successVal=>{
console.log('then2-',successVal) //then2- newP2
},
failValue=>{
console.log('then2-',failValue) //no
}
)
异常捕获和推荐写法、状态固化后的异常、异常冒泡、状态依赖
let promise = new Promise((res,rej)=>{
res(a)
})
promise.then(
success=>{
console.log(1,success)
},
fail=>{
console.log(2,fail)
// 2 ReferenceError: a is not defined
// 所有的错误都在 reject里捕获
}
)
catch捕获reject
promise.then(null,(fail)=>{
console.log('reject',fail)
})
promise.catch((fail) =>{
console.log('reject',fail)
})
推荐写法
promise.then(success=>{
console.log('resolve',success)
})..catch((fail) =>{
console.log('reject',fail)
})
状态固化后无法捕获异常
let promise = new Promise((res,rej)=>{
res('ok')
console.log(123)//这里仍会执行
console.log(a)//异常,但不会走reject
})
promise.then(
success=>{
console.log(1,success)
//123
//1 'ok'
},
fail=>{
console.log(2,fail)
}
)
异常的冒泡特性
let promise = new Promise((res,rej)=>{
console.log(a)
})
promise.then().then().catch((fail) =>{
console.log('reject',fail) //reject ReferenceError: a is not defined
//then()不传递参数的话会直接忽略
})
异步状态依赖
let p1 = new Promise((res,rej)=>{
setTimeout(() => {
rej(new Error('fail')) //VM189:3 Uncaught (in promise) Error: fail
}, 3000);
})
let p2 = new Promise((res,rej)=>{
setTimeout(() => {
res(p1)
}, 1000);
})
// 异步依赖
resolve、reject不会终止函数运行
Promise.all()
方法介绍
方法接收一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,并且只返回一个Promise实例,那个输入的所有promise的resolve回调的结果是一个数组。这个Promise的resolve回调执行是在所有输入的promise的resolve回调都结束,或者输入的iterable里没有promise了的时候。它的reject回调执行是,只要任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。
使用
读取文件是异步 *** 作,假如有3个读取文件的异步 *** 作,等他们全部执行完后希望拿到汇总的数据
const fs = require('fs')
let promise1 = new Promise((resolve,reject)=>{
fs.readFile('./name.txt','utf-8',function(err,data){
if(err){
reject(err)
}
resolve(data)
})
})
let promise2 = new Promise((resolve,reject)=>{
fs.readFile('./number.txt','utf-8',function(err,data){
if(err){
reject(err)
}
resolve(data)
})
})
const p = Promise.all([promise1,promise2])
p.then(res=>console.log(res))
console.log(p)
MDN
const promise1 = Promise.resolve(3);
const promise2 = 42;
//如果参数中包含非 promise 值,这些值将被忽略,但仍然会被放在返回数组中(如果 promise 完成的话):
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'foo');
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// expected output: Array [3, 42, "foo"]
同步
但是,Promise.all 当且仅当传入的可迭代对象为空时为同步:
var p = Promise.all([]); // will be immediately resolved
var p2 = Promise.all([1337, "hi"]); // non-promise values will be ignored, but the evaluation will be done asynchronously
console.log(p);
console.log(p2)
setTimeout(function(){
console.log('the stack is now empty');
console.log(p2);
});
// logs
// Promise { : "fulfilled", : Array[0] }
// Promise { : "pending" }
// the stack is now empty
// Promise { : "fulfilled", : Array[2] }
Promise.all 的快速返回失败行为
Promise.all 在任意一个传入的 promise 失败时返回失败。例如,如果你传入的 promise中,有四个 promise 在一定的时间之后调用成功函数,有一个立即调用失败函数,那么 Promise.all 将立即变为失败。
Promise.race()race 函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。
它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。
如果传的迭代是空的,则返回的 promise 将永远等待。
如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值。
race 顾名思义,哪个Promise先跑赢就直接返回
const promise1 = new Promise((resolve, reject) => {
setTimeout(resolve, 500, 'one');
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, 'two');
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
// Both resolve, but promise2 is faster
});
// expected output: "two"
Promise.resolve()
Promise.resolve(value)
value 将被Promise对象解析的参数,也可以是一个Promise对象,或者是一个thenable。返回值 返回一个带着给定值解析过的Promise对象,如果参数本身就是一个Promise对象,则直接返回这个Promise对象。如果这个值是thenable(即带有"then" 方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;否则返回的promise将以此值完成。此函数将类promise对象的多层嵌套展平。let thenable = {
then: (resolve, reject) => {
// resolve(thenable)
resolve(52)
}
}
let p = Promise.resolve(thenable)
p.then(res=>{
console.log(res)//52
})
使用静态Promise.resolve方法
Promise.resolve("Success").then(function(value) {
console.log(value); // "Success"
}, function(value) {
// 不会被调用
});
resolve一个数组
var p = Promise.resolve([1,2,3]);
p.then(function(v) {
console.log(v[0]); // 1
});
resolve另一个promise
var original = Promise.resolve(33);
var cast = Promise.resolve(original);
cast.then(function(value) {
console.log('value: ' + value);
});
console.log('original === cast ? ' + (original === cast));
/*
* 打印顺序如下,这里有一个同步异步先后执行的区别
* original === cast ? true
* value: 33
*/
Promise.reject()
Promise.reject()方法返回一个带有拒绝原因的Promise对象。
Promise.reject(new Error('fail')).then(function() {
// not called
}, function(error) {
console.error(error); // Stacktrace
});
Promise.reject()不能使用thenable
自定义promisify
和readFile()函数一样,nodejs中很多函数都需要promise化来解决地狱回调的问题fs.readFile(data,‘utf-8’,(err,data)=>{})此类函数的promisify化
function promisify(func){
return function(...args){
return new Promise((resolve,reject)=>{
func(...args,(err,data)=>{
if(err){
reject(err)
}else{
resolve(data)
}
})
})
}
}
node中的promisify工具方法
const util = require('util');
console.log(util.promisify)
// [Function: promisify] { custom: Symbol(nodejs.util.promisify.custom) }
console.log(util.pr)
// 1. 异步方法promisify化
let readFileAsync = util.promisify(fs.readFile)
// ……
// 2. 所有fs上的方法都promisify化
function promisifyAll(obj){
for(let [key,fn] of Object.entries(obj)){
if(typeof fn === 'function'){
obj[key+'Async'] = promisify(fn)
}
}
}
promisifyAll(fs);
fs.readFileAsync()
迭代器
function makeIterator(arr){
var nextIndex = 0;
return {
next(){
if(nextIndex < arr.length){
return {value:arr[nextIndex++],done:false}
}
return {value,undefined,done:true}
}
}
}
var it = makeIterator(['a','b'])
console.log(it,it.next(),it.next())
迭代器模式迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺序访问其中的每个元素。迭代器 内部和外部迭代器
// 外部迭代器 当你得到一个迭代器并跨越它时,这是一个外部迭代器
for (Iterator iter = var.iterator(); iter.hasNext(); ) {
Object obj = iter.next();
// Operate on obj
}
// 内部迭代器 当你将一个函数对象传递给一个方法来遍历一个列表时,这是一个内部迭代器
var.each( new Functor() {
public void operate(Object arg) {
arg *= 2;
}
});
对象配置迭代器接口
array map set string TypeArray NodeList arguments 这些都实现了迭代器接口,只有object没有
let obj = {
star:[1,2,3],
end:[7,8,9],
[Symbol.iterator](){
var nextIndex = 0;
var arr = [...this.star,...this.end];
var len = arr.length;
return {
next(){
if(nextIndex<len){
return {value:arr[nextIndex++],done:false}
}else{
return {undefined,done:true}
}
}
}
}
}
// 对象本身不具备迭代器接口,给他加上迭代器接口就可以用of进行遍历
// 为什么对象本身不具备迭代器接口?
// 因为迭代器模式是有序访问聚合对象中的各个元素,而对象本身是无序的
for(let i of obj){
console.log(i);
}
map
let map = new Map([ ['a',1],['b',2] ])
console.log(map)
// Map(2) {'a' => 1, 'b' => 2}
// [[Entries]]
// 0: {"a" => 1}
// 1: {"b" => 2}
for(let m of map){
console.log(m)
// (2) ['a', 1]
// (2) ['b', 2]
}
for(let [k,v] of map){
console.log(k,v)
}
map迭代器
let obj = {
a:1,
b:2,
c:3,
// [[a,1],[b,2],[c,3]]
[Symbol.iterator](){
let nextIndex = 0
let map = new Map();
for(let [k,v] of Object.entries(this)){
console.log(k,v)
map.set(k,v)
}
console.log(map);
let mapEntries = [...map.entries()];
console.log(mapEntries);
return {
next(){
return nextIndex<map.entries.length ?
{value: mapEntries[nextIndex++],done:false}:
{value: undefined,done:true}
},
return(){
// 遍历中break,报错等终止for循环后 走这儿
console.log(1,'return')
return {value:1,done:false}
}
}
}
}
for(let i of obj){
console.log(i)
// break
throw new Error('hello')
}
iterator默认调用迭代器接口的场合
…扩展运算符for ofArray.from() 生成器函数 函数声明function 和 函数名 中间具有分号时,为生成器函数,他返回一个迭代器对象
function * test(){}
function* test(){}
function *test(){}
yield 关键字
生成器函数的返回值是迭代器
yield可以产出相同的值
function * test2(){
let b = [1,2]
yield b;
yield b;
}
let it = test2()
let next1 = it.next().value;
let next2 = it.next().value
console.log(next1)
console.log(next2)
console.log(next1===next2)
yield有记忆功能,可以暂停函数,return是函数终止
生成器里面不能有break
yield 只能出现在生成器函数中
function * test(){
yield 'a';
console.log(1);
yield 'b';
return 'c';
}
let it = test()
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())
// {value: 'a', done: false} 如果产出的值为yield,done为false
// 1
// {value: 'b', done: false}
// {value: 'c', done: true} 如果产出的值为return,done为true
// {value: undefined, done: true}
yield的返回值是由next决定的
function * test2(){
let a = yield 'a';
console.log(a);//undefined
yield 'b';
return 'c';
}
let it = test2()
console.log(it.next())
console.log(it.next())
// =============
function * test2(){
let a = yield 'a';
console.log(a);//10
yield 'b';
return 'c';
}
let it = test2()
console.log(it.next())
console.log(it.next(10))
// yield并不产出值,他的参数由next决定
//=======================
function * foo(){
let value1 = yield 1;
console.log('value1:',value1);
let value2 = yield 2;
console.log('value2:',value2);
let value3 = yield 3;
console.log('value3:',value3);
}
let it = foo();
console.log(it.next())
console.log(it.next('two'))
console.log(it.next('three'))
console.log(it.next('four'))
yield 是一个单独的表达式,作为子表达式时需要加括号
function * demo(){
console.log('demo123')
}
let it = demo()
console.log(it.next())
function * demo2(){
// yield 是一个单独的表达式,作为子表达式时需要加括号
console.log('hello',(yield 123))
}
let it = demo2()
console.log(it.next()) // {value: 123, done: false}
console.log(it.next()) //hello {value: undefined, done: true}
生成器中只遍历yield,不会遍历return的值
function * foo(){
yield 1;
yield 2
return 3;
}
for(let i of foo()){
console.log('test',i)
}
// test 1
// test 2
// 遍历的时候不会遍历return的值
使用生成器配置迭代器接口
let obj = {
star:[1,2,3],
end:[7,8,9],
// [Symbol.iterator](){
[Symbol.iterator]: function* (){
var nextIndex = 0;
var arr = [...this.star,...this.end];
var len = arr.length;
// return {
// next(){
// if(nextIndex
// return {value:arr[nextIndex++],done:false}
// }else{
// return {undefined,done:true}
// }
// }
// }
while(nextIndex < len){
yield arr[nextIndex++]
}
}
}
// 对象本身不具备迭代器接口,给他加上迭代器接口就可以用of进行遍历
// 为什么对象本身不具备迭代器接口?
// 因为迭代器模式是有序访问聚合对象中的各个元素,而对象本身是无序的
for(let i of obj){
console.log(i);
}
生成器解决地狱回调
const fs = require('fs');
const util = require('util')
const readFile = util.promisify(fs.readFile);
function * read(){
let value1 = yield readFile('./name.txt','utf-8');
let value2 = yield readFile(value1,'utf-8');
let value3 = yield readFile(value2,'utf-8');
console.log(value3)
}
let iter = read();
// 1. =====================
// let {value,done} = iter.next();
// value.then((res1)=>{
// // console.log(res1)
// // iter.next(res1)
// let {value,done} = iter.next(res1);
// value.then(res2=>{
// let {value,done} = iter.next(res2);
// value.then(res3=>{
// console.log(res)
// })
// })
// })
// 2. =======优化=========
function Co(iter){
return new Promise((resolve,reject)=>{
let next = (data)=>{
let {value,done} =iter.next(data);
if(done){
res(value)
}else{
value.then((res)=>{
next(res)
})
}
}
next()
})
}
let p = Co(read)
p.then((res)=>{
console.log(res)
})
// 3.====================
// Co也是npm中的一个同名包
// TJ koa co express jade mocha
// let co = require('co')
// let p = co(read)
// p.then((res)=>{
// console.log(res)
// })
// Co也是async的由来
async function read(){
let value1 = await readFile('./name.txt','utf-8');
let value2 = await readFile(value1,'utf-8');
let value3 = await readFile(value2,'utf-8');
console.log(value3)
}
生成器返回的迭代器中的方法
next
return
function * get(){
yield 1;
// 2. r
// return 10
yield 2;
yield 3;
}
let g = get();
console.log(g.next())
// 迭代器的return方法相当于生成器中显示调用return方法
// return 可以产出值,也会终止函数,done->true 已经完成
// return 后面next的value->undefined
// 1. r
console.log(g.return(10))
console.log(g.next())
console.log(g.next())
console.log(g.next())
throw
try catch 只能捕获同步异常,不能捕获异步异常
function * get(){
yield 1;
try {
yield 2;
} catch (error) {
console.log('生成器内部异常',error)
}
yield 3;
console.log('end')
}
let g = get()
// 1.
// console.log(g.throw('a')) //VM166:1 Uncaught a 此时为全局异常 抛出错误,生成器无法捕获
// 2.
console.log(g.next())
console.log(g.next())
// ----------------
console.log(g.throw('a'))
// 抛出异常,生成器内部异常 a
// {value: 3, done: false}
// throw = 抛出错误 + next 两层左右
// ----------------
console.log(g.throw('b')) ///VM166:1 Uncaught a ->全局异常
console.log(g.next())
console.log(g.next())
// 生成器函数中可以直接捕获异步代码的异常
let fs = require('fs')
let util = require('util')
let co = require('co')
let readFile = util.promisify(fs.readFile)
function * read(){
// yield fs.readFile()
// yield异步代码 必须用promise包裹
try {
let value1 = yield readFile('./name.txt','utf-8')
let value2 = yield readFile(value1,'utf-8')
let value3 = yield readFile(value2,'utf-8')
} catch (error) {
console.log('异步',error)
}
console.log('hello end')
}
let p = co(read());
p.then(res=>{
console.log(res)
})
async
内置的执行器co更好的语义更广的实用性返回值一定是promise无论如何显示地返回,最后的返回值会再次被promise包装
let fs = require('fs')
let util = require('util')
let co = require('co')
let readFile = util.promisify(fs.readFile)
async function read() {
let value1 = await readFile('./name.txt', 'utf-8')
let value2 = await readFile(value1, 'utf-8')
let value3 = await readFile(value2, 'utf-8')
console.log('hello end')
}
// let p = co(read());
let p = read();
p.then(res => {
console.log(res)
})
错误捕获
async function test(){
// var value = '1';
var value = await '1';
// console.log(a)
// console.log('after err') //出错后会终止程序执行
console.log(value) //1
// 可以用try catch 捕获
try {
console.log(a)
} catch (error) {
console.log('try',error)
}
console.log('after err')
return value;
}
let t = test()
console.log(t) //Promise {: '1'}
t.then(res=>{
console.log('then',res) //then 1
},rej=>{
console.log('err',rej) // err ReferenceError: a is not defined
})
应用场景
遍历和迭代
遍历和迭代都是循环输出集合中的元素遍历的过程是不可控制的迭代的过程是可控制的,可以随时暂停,随时开始,相当于断点续传生成器就是一个可以返回迭代器的函数
var arr = [1,2,3,4,5]
for (const item of arr) {
console.log(item)
}
// generator 生成器->迭代器 iterator
function * test(arr){
for (const item of arr) {
yield item
}
}
let iter = test(arr)
console.log(iter.next())
var functions = [
function test1(next) {
console.log('test1')
next()
},
function test2(next) {
console.log('test2')
next()
// next() 没有next 就会到此阶段
}
,
function test3(next) {
console.log('test3')
next()
}
]
; (function () {
function* generator(arr) {
for (let index = 0; index < arr.length; index++) {
yield arr[index];
}
}
const iterator = generator(functions);
const init = () => {
nextDo(iterator.next())
}
function nextDo(n) {
// n.value 传递进来的函数集合参数
n.value(
function () {
const n = iterator.next();
if (!n.done) {
nextDo(n)
} else {
return;
}
}
)
}
init()
})()
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)