函数式编程是编程范式之一,还有我们熟悉的面向过程和面向对象编程。
// 非函数式
let num1 = 1;
let num2 = 2;
let sum = num + num1;
console.log(sum);
// 函数式
function add(a,b) {
return a+ b
}
console.log(add(1,2));
在Javascript中函数就是一个普通对象(可以通过 new Function()),我们可以把函数存储到变量中/数组中,他还可以作为一个函数的参数和返回值,甚至我们可以在远程运行的时候通过new Function(‘alert(1)’)来构造一个新函数。
// 把函数赋值给变量
let fn = function(){
console.log('hello world');
}
// 例子
const BlogController = {
index(posts){return views.index(posts)},
show(post){return views.show(post)},
create(attrs){return Db.create(attrs)},
update(post,attrs){return Db.update(post,attrs)},
destory(post){return Db.destory(post)},
}
// 简写优化
// 函数里面包裹另一个函数形式类似并返回,那么就可以认为这两个函数是同一个
const BlogController = {
index: views.index,
show: views.show,
create: Db.create,
update: Db.update,
destory: Db.destory
}
什么是高阶函数
函数作为参数
// 函数作为参数
function forEach(arr, fn){
for (const item of arr) {
fn(item)
}
}
let arr1 = [1,2,3,4,6]
forEach(arr1,(item) => {
console.log(item);
})
// 函数作为参数
function filters(arr, fn){
let result = []
for (const item of arr) {
if (fn(item)) {
result.push(item)
}
}
return result
}
let arr2 = [2,3,5,6,8,9]
const newArr = filters(arr2, (item) => {
if (item % 2 === 0) {
return false
} else {
return true
}
})
console.log(newArr);
// 函数作为返回结果
function makeFn() {
const str = 'hello world'
return function() {
console.log(str);
}
}
const fn = makeFn();
fn();
// 也可以直接调用
// makeFn()();
// 函数作为返回结果,而且只执行一次
function once(fn) {
let flag = false;
return function (){
if(!flag){
flag = true;
// 就是return的这个函数的参数也就是pay(9)中的参数9,虽然return的这个函数的没有使用形参接收但是通过arguments还是能获取到的
// apply通俗一点讲就是:用return的这个函数去执行fn里面的内容.
return fn.apply(this,arguments)
}
}
}
const pay = once((money) => {
console.log(`支付:${money}RMB`);
})
// 只会支付一次
pay(9);
pay(2);
pay(6);
// 函数作为返回值
function makeFn() {
const str = 'hello world'
return function() {
console.log(str);
}
}
const fn = makeFn();
fn();
// 也可以直接调用
// makeFn()();
// 函数作为返回值,而且只执行一次
function once(fn) {
let flag = false;
return function (){
if(!flag){
flag = true;
// 就是return的这个函数的参数也就是pay(9)中的参数9,虽然return的这个函数的没有使用形参接收但是通过arguments还是能获取到的
// apply通俗一点讲就是:用return的这个函数去执行fn里面的内容.
return fn.apply(this,arguments)
}
}
}
const pay = once((money) => {
console.log(`支付:${money}RMB`);
})
// 只会支付一次
pay(9);
pay(2);
pay(6);
// 面向过程的方式
// 这种方式我们在遍历的时候还要去关注 i的大小等等这些细节
let array = [1, 2, 3, 4]
for (let i = 0; i < array.length; i++) {
console.log(array[i])
}
// 高阶函数
function forEach(arr, fn){
for (const item of arr) {
fn(item)
}
}
// 而我们封装好的高阶函数在使用的时候就不用去关注i的大小等这些细节,因为内部已经封装好了
let arr1 = [1,2,3,4,6]
forEach(arr1,(item) => {
console.log(item);
})
function filters(arr, fn){
let result = []
for (const item of arr) {
if (fn(item)) {
result.push(item)
}
}
return result
}
let arr2 = [2,3,5,6,8,9]
const newArr = filters(arr2, (item) => {
if (item % 2 === 0) {
return false
} else {
return true
}
})
console.log(newArr);
// 手写高阶函数
// 把一些个性化的需求提取出来比如map是乘以2还是除以2这些交给调用者去个性化制定
// map是对每一项进行处理
function map (arr,fn) {
let result = [];
for (const item of arr) {
result.push(fn(item))
}
return result
}
let arr1 = [1,5,6,7]
const newArr = map(arr1,(item) => {
return item * 2
})
console.log(newArr);
// 只要有一个满足条件就返回true
function some(arr, fn){
let flag = false;
for (const item of arr) {
// 只要有一个满足就可以退出循环了
if (fn(item)) {
flag = true;
break
}
}
return flag
}
let arr2 = [4,5,6,3,9]
const flag2 = some(arr2, (item) => {
return item < 1
})
console.log(flag2);
// 所有满足条件才返回true,只要有一个不满足就返回false
function every(arr, fn){
let flag = true;
for (const item of arr) {
// 只要有一个不满足就可以退出循环了
if (!fn(item)) {
flag = false;
break
}
}
return flag
}
let arr3 = [4,5,2,8,9]
const flag3 = every(arr3, (item) => {
return item < 10
})
console.log(flag3);
// 这个例子中当make执行完后这个函数的内部成员就会被释放掉。
function make() {
const str = 'hello world';
}
make();
// 但是当一个函数返回了另一个函数并且访问了外部函数的成员的话,就不会被释放掉
function makeFn() {
const str = 'hello world'
return function() {
console.log(str);
}
}
// 这里调用了makeFn而fn其实就是返回来的那个函数,而返回来的内部函数访问了外部函数的str这个成员,所以就不会被释放掉
const fn = makeFn();
fn();
// 也可以直接调用
// makeFn()();
// 函数作为返回值,而且只执行一次
function once(fn) {
let flag = false;
return function (){
if(!flag){
flag = true;
// 就是return的这个函数的参数也就是pay(9)中的参数9,虽然return的这个函数的没有使用形参接收但是通过arguments还是能获取到的
// apply通俗一点讲就是:用return的这个函数去执行fn里面的内容.
return fn.apply(this,arguments)
}
}
}
const pay = once((money) => {
console.log(`支付:${money}RMB`);
})
// 只会支付一次
pay(9);
pay(2);
pay(6);
案例:生成计算数字的多少次幂的函数
// 闭包的案例
function makePower(power){
return function(num){
return Math.pow(num, power)
}
}
// 使用闭包可以封装一些较为常用的方法,例如我们在使用Math.pow求一个数的n次幂时,有一些求平方和立方是使用频率比较多的,但是我们每次使用时都要传递两个参数这样还是比较麻烦的所以我们可以通过闭包进行封装出一个方法专门用于求平方和立方,封装后只需要传递需要求幂的数字这一个参数就可以了
const makePower2 = makePower(2);
const makePower3 = makePower(3);
// 调用函数内部的返回的函数形成闭包
const result2 = makePower2(9);
const result3 = makePower3(9);
console.log(result2);
console.log(result3);
// 纯函数
let number = [1,2,4,6,7,4];
// 截取从下标0到3的值(不包括下标3)并返回截取部分的数组,不会改变原数组
number.slice(0,3);
// => [1,2,4]
number.slice(0,3);
// => [1,2,4]
number.slice(0,3);
// => [1,2,4]
// 非纯函数
let number2 = [1,2,4,6,7,4];
// 截取下标0开始截取3个,改变原数组,返回被截取部分的数组
number2.splice(0, 3);
// => [1,2,4]
number2.splice(0, 3);
// => [6,7,4]
number2.splice(0, 3);
// => []
// 手写一个纯函数
function add(a,b){
return a + b;
}
add(1,2);
// => 3
add(1,2);
// => 3
add(1,2);
// => 3
Lodash是一个一致性、模块化、高性能的JavaScript实用工具库(lodash 的 fp 模块提供了对函数
式编程友好的方法),提供了对数组、数字、对象、字符串、函数等操作的一些方法,这些都是纯函数。
const _ = require('lodash');
let arr = [4,1,6,3,4,5]
// 查找第一个并返回
const result1 = _.first(arr)
// console.log(result1);
// 循环,参数一是要循环的参数,参数二是个函数:itme每一项,index索引;返回循环的函数
const result2 = _.each(arr,(itme, index) => {
// console.log(itme);
console.log(index);
})
console.log(result2);
// 转成大写
const result3 = _.toUpper(_.first(arr))
// 记忆函数:计算了一次就会把结果缓存下来,不会在进行计算
const _ = require('lodash');
// 计算园面积的函数
function getArea(r){
console.log(r);
return Math.PI * r * r
}
// 接收一个函数为参数,返回一个纯函数
// const getAreaFn = _.memoize(getArea);
// console.log(getAreaFn(4));
// console.log(getAreaFn(4));
// console.log(getAreaFn(4));
// console.log(getAreaFn(4));
// 自己手写memoize
// 首先他接收一个函数作为参数,还会返回一个函数,再则要判断是否已经计算了,计算了的话,返回一个结果,否则就执行传递过来的函数,再把执行结果返回
function memoize(fn) {
let obj = {}
const key = JSON.stringify(arguments)
return function(){
obj[key] = obj[key] || fn.apply(fn, Array.from(arguments))
return obj[key]
}
}
const getAreaFn = memoize(getArea);
console.log(getAreaFn(4));
console.log(getAreaFn(4));
console.log(getAreaFn(4));
console.log(getAreaFn(4));
// 不纯的
// 因为函数依赖外部变量mini,所以即使我们传入相同的参数但是只要改变了外部变量mini也就得不到相同的结果了
let mini = 18
function checkAge (age) {
return age >= mini
}
// 纯函数
// 把mini提取到函数内部去,就变成纯函数了(有硬编码,后续可以通过柯里化解决)
// 就是我们平常讲的写死
function checkAge (age) {
let mini = 18
return age >= mini
}
副作用会让一个函数变得不纯,纯函数根据相同的输入得到相同的输出,如果函数内部依赖于外部的状态就无法保证输出相同,就会带来副作用。
副作用的来源
// 有硬编码的纯函数
function checkAge (age) {
let mini = 18
return age >= mini
}
// 使用函数柯里化决解硬编码
function checkAge(mini){
return function(age){
return age >= mini
}
}
const checkAge18 = checkAge(18);
const checkAge20 = checkAge(20);
console.log(checkAge18(15));
console.log(checkAge18(30));
console.log(checkAge20(15));
console.log(checkAge20(30));
// 案例
// lodash中的柯里化函数
const _ = require('lodash');
function getSum(a, b, c){
return a + b + c
}
const curried = _.curry(getSum);
console.log(curried(1,2,3)); // 6
console.log(curried(1)(2,3)); // 6
console.log(curried(1,2)(3)); // 6
// lodash的curry函数
// const _ = require('lodash');
// function getSum(a, b, c){
// return a + b + c
// }
// const curried = _.curry(getSum);
// console.log(curried(1,2,3)); // 6
// console.log(curried(1)(2,3)); // 6
// console.log(curried(1,2)(3)); // 6
// 自己实现curry函数
/*
首先curry要接收一个参数fn函数,而这个参数fn函数的参数的个数就是我们需要的形参个数,
然后返回一个柯里化后的函数curried,而这个函数的参数就是我们实际传递的实参个数,
当实参个数小于形参个数时会再次返回一个柯里化后的函数curriedFn传递过去的参数则是上一次加上当前传递的参数的合并的值,此时并不会调用fn,直到实参大于等于形参后才真正调用fn这个函数返回结果
*/
function getSum(a, b, c){
return a + b + c
}
function curry(fn){
return function curriedFn(...args){
// 如果实参小于形参
if (args.length < fn.length) {
return function(){
// 第一次调用的参数会保存在args,当再次调用的时候是返回的这个匿名函数,
// 这是我们通过arguments就能再次获取到参数,然后把上一次调用的参数args和返回后再次调用的这个匿名函数的参数arguments进行合并后
// 再次调用curriedFn
return curriedFn(...args.concat(Array.from(arguments)))
}
}
// 实参不小于形参后就执行fn并返回结果
return fn(...args)
}
}
const curried = curry(getSum);
console.log(curried(1,2,3)); // 6
console.log(curried(1,2)(3)); // 6
console.log(curried(1)(3)(2)); // 6
纯函数和柯里化很容易写出洋葱代码即h(g(f(x)))
函数组合刻意让我们把细粒度的函数重新组合生成一个新的函数
函数组合:如果一个函数要经过多个函数处理才能得到最终值,这个时候可以把中间过程的函数合并到一个函数
// 函数组合
function compose(f,g){
return function(val){
return f(g(val))
}
}
function reverse(arr){
return arr.reverse()
}
function first(arr){
return arr[0]
}
let arr = [1,2,3,4,5]
const last = compose(first,reverse);
console.log(last(arr));
// lodash中的函数组合
const _ = require('lodash');
const reverse = arr => arr.reverse()
const first = arr => arr[0];
const toUpperCase = str => str.toUpperCase()
// 调用flowRight会返回一个函数UpperCase
const UpperCase = _.flowRight(toUpperCase,first,reverse);
// 调用UpperCase并传递要操作的数据
const result = UpperCase(['nihao','hello','world'])
console.log(result);
// // lodash中的函数组合
// const _ = require('lodash');
// const reverse = arr => arr.reverse()
// const first = arr => arr[0];
// const toUpperCase = str => str.toUpperCase()
// // 调用flowRight会返回一个函数UpperCase
// const UpperCase = _.flowRight(toUpperCase,first,reverse);
// // 调用UpperCase并传递要操作的数据
// const result = UpperCase(['nihao','hello','world'])
// console.log(result);
// 自己写实现lodash中的flowRight方法
// function compose(...args){
// return function(val){
// // acc累计器是上次执行回调的结果,没有默认值的话第一次默认为数组的第一项,这里默认的是val也就是用户传进来的数据,fn数组的当前项
// return args.reverse().reduce(function(acc,fn){
// return fn(acc)
// },val)
// }
// }
// 箭头函数写法
const compose = (...args) => val => args.reverse().reduce((acc,fn) => fn(acc),val)
const reverse = arr => arr.reverse()
const first = arr => arr[0];
const toUpperCase = str => str.toUpperCase()
// 调用flowRight会返回一个函数UpperCase
const UpperCase = compose(toUpperCase,first,reverse);
// 调用UpperCase并传递要操作的数据
const result = UpperCase(['nihao','hello','world'])
console.log(result);
// 函数组合的结合律
const _ = require('lodash')
// 前提条件是总的顺序不能乱,虽然进行了不同的结合但是总的顺序还是toUpper,first,reverse所以都是没问题的
// const UpperCase = _.flowRight(_.toUpper,_.first,_.reverse);
// const UpperCase = _.flowRight(_.toUpper,_.flowRight(_.first,_.reverse));
const UpperCase = _.flowRight(_.toUpper,_.first,_.flowRight(_.reverse));
// 而下面是reverse,toUpper,first总的顺序乱了所以会报错
// const UpperCase = _.flowRight(_.reverse,_.flowRight(_.toUpper,_.first));
// 调用UpperCase并传递要操作的数据
const result = UpperCase(['nihao','hello','world'])
console.log(result);
// 转换 NEVER SAY DIE => never-say-die
const _ = require('lodash');
/**
* 思路: 转换成上面需要先把split按指定符号把字符串切割成数组[ 'NEVER', 'SAY', 'DIE' ],
* 然后通过_.toLower转成“never,say,die”,在通过map转成数组[ 'never', 'say', 'die' ]
* 最后通过join把数组按‘-’转成字符串never-say-die
*/
// 当使用函数组合时报错了怎么打印报错信息。可通过以下方法
// 当打印地方多时,这种打印结果不容易分辨
// function log(v){
// console.log(v);
// return v
// }
// 解决了打印地方多,不容易分辨的问题即传递表示过去
const trace = _.curry((str,v) => {
console.log(str,v);
return v
})
// 为什么要使用柯里化比且还把行参的顺序进行了调换例如sep,str 换成str,sep
// 因为组合函数只能接收一个参数的纯函数,使用柯里化就可以以把多元函数转换成一元函数也就是一个参数的函数,至于把形参调换是因为在函数组合完成后才会调用这个函数的时候才会传入字符串
const split = _.curry((sep,str) => _.split(str,sep));
const join = _.curry((str,arr) => _.join(arr,str));
const map = _.curry((arr,fn) => _.map(fn,arr));
// flowRight的执行顺序从右往左,会先执行split(' '),因为这是个柯里化后的函数,他接收两个参数,但是目前只传了一个所以还是会返回一个函数,其他也是这样所以最终flowRight的参数都是一个一元函数
const f = _.flowRight(join('-') ,trace("map之后"),map(_.toLower),trace("split之后"),split(' '));
// 最后在调用这个组合函数并传递字符串,此时像split这些柯里化后的函数就会执行并返回结果了,最后打印出来
console.log(f('NEVER SAY DIE'));
在我们上一节使用函数组合时,是自己对lodash的一些方法进行了封装,lodash中的方法没有进行柯里化以及这些方法的参数是数据优先,函数最后。如果我们每次使用lodash进行函数组合都要自己在进行一次封装的话就很麻烦,Lodash-fp模块就是解决这一问题的
// lodash 模块
const _ = require('lodash')
_.map(['a', 'b', 'c'], _.toUpper)
// => ['A', 'B', 'C']
_.map(['a', 'b', 'c'])
// => ['a', 'b', 'c']
_.split('Hello World', ' ')
// lodash/fp 模块
// const fp = require('lodash/fp')
// fp.map(fp.toUpper, ['a', 'b', 'c'])
// fp.map(fp.toUpper)(['a', 'b', 'c'])
// fp.split(' ', 'Hello World')
// fp.split(' ')('Hello World')
// 使用lodash/fp 模块 转换 NEVER SAY DIE => never-say-die
const fp = require('lodash/fp');
// flowRight的执行顺序从右往左,会先执行split(' '),因为这是个柯里化后的函数,他接收两个参数,但是目前只传了一个所以还是会返回一个函数,其他也是这样所以最终flowRight的参数都是一个一元函数
const f = fp.flowRight(fp.join('-'),fp.map(fp.toLower),fp.split(' '));
// 最后在调用这个组合函数并传递字符串,此时像split这些柯里化后的函数就会执行并返回结果了,最后打印出来
console.log(f('NEVER SAY DIE'));
// lodash 模块的map方法
const _ = require('lodash');
/**
* 这是因为lodash的map接收两个参数分别是数组和回调函数parseInt需要传递3个参数分别是当前处理项、索引、当前数组,
* 而第二个参数表示要解析的数字的基数是或其值为 0,则数字将以 10 为基础来解析。
* 如果它以 “0x” 或 “0X” 开头,将以 16 为基数。如果该参数小于 2 或者大于 36,则 parseInt() 将返回 NaN
*/
console.log(_.map(['32','54','4'],parseInt)); // [ 32, NaN, NaN ]
// lodash/fp 模块的map方法
const fp = require('lodash/fp');
// 这是因为lodash/fp的回调函数parseInt直接收一个参数即当前处理项
console.log(fp.map(parseInt, ['32','54','4'])); // [ 32, 54, 4 ]
Point Free:把数据处理的过程定义成与数据无关的合成运算,不需要用到代表数据的那个参数,只要把简单的运算步骤合成到一起,在使用这种模式之前我们需要定义一些辅助的基本运算函数。
// Hello World => hello_world
// 非 Point Free 模式
// Hello World => hello_world
function f (word) {
return word.toLowerCase().replace(/\s+/g, '_');
}
// Point Free 模式
const fp = require('lodash/fp');
// 在组合函数的时候不需要指明处理的数据
const f = fp.flowRight(fp.replace(/\s+/g,'_'),fp.toLower)
// 调用的时候再传递数据
console.log(f('Hello World'));
// Pointfree 案例1
// 'world wild web' => W. W. W
// const fp = require('lodash/fp');
// const result = fp.flowRight(fp.join('. '),fp.map(fp.first),fp.map(fp.toUpper),fp.split(' '));
// console.log(result('world wild web')); // W. W. W
// 上述代码相当于是下面的代码(便于理解)
// const fp = require('lodash/fp');
// const str = 'world wild web';
// // 返回字符串数组
// const aa = str.split(' ');
// const bb = aa.map((item,index,arr) =>{
// return fp.toUpper(item)
// })
// const cc = bb.map((item,index,arr) =>{
// return fp.first(item)
// })
// const dd = fp.join('. ')
// console.log(aa); // [ 'world', 'wild', 'web' ]
// console.log(bb); // [ 'WORLD', 'WILD', 'WEB' ]
// console.log(cc); // [ 'W', 'W', 'W' ]
// console.log(dd(cc)); // W. W. W
// 优化案例1
// 这里使用了两次map去遍历数组,可以再次使用flowRight函数组合成一次
const fp = require('lodash/fp');
const result = fp.flowRight(fp.join('. '),fp.map(fp.flowRight(fp.first,fp.toUpper)),fp.split(' '));
console.log(result('world wild web')); // W. W. W
我们之前学的都是函数式编程的一些基础,但是我们还没有演示在函数式编程中如何把副作用控制在可控的范围内、异常处理、异步操作等。
// 函子
// 函子是一个特殊的容器通过普通对象即这里的Container来实现,这个对象有一个map方法
// 构造函数创建一个私有属性_value进行接收值即这里的5。map方法就是那个变形关系,map内部会返回一个有Container创建的对象传递的参数是通过调用map传递的一个函数用于处理并返回数据即fn(this._value)
// class Container {
// constructor(value){
// this._value = value;
// }
// map(fn){
// return new Container(fn(this._value))
// }
// }
// const result = new Container(5)
// .map(x => x + 1)
// .map(x => x * x)
// console.log(result); // Container { _value: 36 }
// 我们使用的是函数式编程但是上述看着像是面向对象编程,所以可以改进下
class Container {
// 静态方法可以直接调用
static of (value){
return new Container(value)
}
constructor(value){
this._value = value;
}
map(fn){
return Container.of(fn(this._value))
}
}
// 这样调用时就没有使用new关键字了,虽然Container类里面还是有,但是那是封装的过程
const result = Container.of(5).map(x => x + 1).map(x => x * x)
console.log(result); // 36
上一节中的代码如果我们传入null或undefined
class Container {
static of (value){
return new Container(value)
}
constructor(value){
this._value = value;
}
map(fn){
return Container.of(fn(this._value))
}
}
// 不小心传入一个空值,此时就会报错,也就破环了纯函数,因为纯函数是相同输入得到相同输出,但是此时却报错了并没有输出,这就是副作用
const result = Container.of(null)
.map(x => x.toUpperCase())
console.log(result);
// MayBe 函子
class MayBe {
static of (value){
return new MayBe(value)
}
constructor(value){
this._value = value;
}
map(fn){
return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
}
// 辅助函数用于判断传递的参数是否等于null或undefined
isNothing(){
return this._value === null || this._value === undefined;
}
}
// 不小心传入一个空值,此时就会报错,也就破环了纯函数,因为纯函数是相同输入得到相同输出,但是此时却报错了并没有输出,这就是副作用
const result = MayBe.of(null)
.map(x => x.toUpperCase())
console.log(result); // MayBe { _value: null }
// MayBe 函子
class MayBe {
static of (value){
return new MayBe(value)
}
constructor(value){
this._value = value;
}
map(fn){
return this.isNothing() ? MayBe.of(null) : MayBe.of(fn(this._value))
}
// 辅助函数用于判断传递的参数是否等于null或undefined
isNothing(){
return this._value === null || this._value === undefined;
}
}
// 在调用第二个map中返回一个null,打印并没有具体指出是哪一步的问题
const result = MayBe.of('hello world')
.map(x => x.toUpperCase())
.map(x => null)
.map(x => x.split(' '))
console.log(result);
// => MayBe { _value: null }
其实就是分别写两个函子然后跟据try…catch捕捉的结果而选择性的调用不同函子
// Either 函子
// 处理错误的函子
class Left {
static of (value){
return new Left(value)
}
constructor(value){
this._value = value;
}
map(){
return this
}
}
// 处理正确的函子
class Right {
static of (value){
return new Right(value)
}
constructor(value){
this._value = value;
}
map(fn){
return Right.of(fn(this._value))
}
}
function parseJSON(val){
try {
return Right.of(JSON.parse(val))
} catch (e) {
// 错误直接返回错误信息
return Left.of({error: e.message})
}
}
// 成功时
const f = parseJSON('{"name":"zf"}').map(x => x.name.toUpperCase())
console.log(f); // Right { _value: 'ZF' }
// 错误时
const f1 = parseJSON('{name:zf}').map(x => x.name.toUpperCase())
console.log(f1); // Left { _value: { error: 'Unexpected token n in JSON at position 1' } }
const fp = require('lodash')
// IO 函子
// 处理正确的函子
class IO {
static of (x){
console.log(1);
return new IO(function(){
console.log(4);
return x
})
}
// fn是of方法中调用IO构造函数是传入的函数
constructor(fn){
console.log(2);
this._value = fn;
}
map(fn){
console.log(3);
console.log(fp.flowRight(fn,this._value)); // [Function]
// 这里不是调用函数处理值而是把当前value和传进来的fn即p => p.execPath组合成一个新的函数,所以布调用of方法而是IO构造函数
return new IO(fp.flowRight(fn,this._value))
}
}
// node对象process进程
/**
* 当我们调用of的时候,他会把当前我们取值的的过程包装到一个函数里面,
* 当我们需要的时候来获取这个process。然后调用map方法来获取process中的某个属性,
* map参数p其实就是of中传入的这个值
*/
const f = IO.of(process).map(p => p.execPath);
console.log(f._value()); // C:\Program Files\nodejs\node.exe
const fp = require('lodash')
// Folktale的compose、curry的使用
const {compose, curry} = require('folktale/core/lambda')
function fn(a,b){
return a + b
}
// 和lodash中的curry不同的是folktale的curry第一个参数是传入函数的参数个数
const f = curry(2,fn);
console.log(f(1,2)); // 3
const c = compose(fp.toUpper,fp.first);
console.log(c(['one', 'two'])); // ONE
// Task 处理异步任务
const fs = require('fs');
const {split, find} = require('lodash/fp')
const {task} = require('folktale/concurrency/task');
function readFile(filename){
// 固定参数resolver对象有两个方法 reject和resolve
return task(resolver => {
// node里面的回调函数的参数是函数优先然后是数据
fs.readFile(filename,'utf-8', (err,data) =>{
if (err) resolver.reject(err);
resolver.resolve(data)
})
})
}
readFile('package.json')
.map(split('\n'))
.map(find(v => v.includes('version')))
.run()
.listen({
// listen是监听函数:onRejected监听失败的函数,onResolved监听成功的函数
onRejected: (err) => {
console.log(err);
},
onResolved: (data) => {
console.log(data);
}
})
// Pointed 函子
class Container {
static of (value) {
return new Container(value)
}
// ……
}
// 这个返回的结果就是上下文,将来在这个上下文中处理数据
Contanier.of(2)
.map(x => x + 5)
const fp = require('lodash');
const fs = require('fs');
class IO {
static of(){
return new IO(function(x) {
return x
})
}
constructor(fn){
this._value = fn
}
map(fn){
return new IO(fp.flowRight(fn,this._value))
}
}
function readFile(filename){
/**
* 直接读取文件会有副作用让函数变得不纯,
* 所以不直接读取文件而是返回一个IO函子,
* 也就是让读取文件延迟去执行
*/
return new IO(function() {
return fs.readFileSync(filename,'utf-8')
})
}
function print(x){
return new IO(function(){
console.log(x);
return x
})
}
// cat的格式其实是 IO(IO(x));里面的IO就是print这个函数返回的函子外层的IO是readFile返回的函子
const cat = fp.flowRight(print,readFile);
// 这样调用不太好,所以需要继续优化
console.log(cat('package.json')._value()._value());
const fp = require('lodash');
const fs = require('fs');
class IO {
static of(){
return new IO(function(x) {
return x
})
}
constructor(fn){
this._value = fn
}
/**
* 调用时把fn、this._value进行合并,然后返回一个IO函子
*
*/
map(fn){
return new IO(fp.flowRight(fn,this._value))
}
/**
* 因为IO函子在创建的时候需要接收一个函数,
* 而当这个函数返回一个函子的时候我们就需要把它变成Monad函子。
* 调用并返回一个函子
* 写join的目的就是为了解决上一小节的问题
*/
join(){
return this._value()
}
/**
* 作用是同时调用map和join.
* 需要调用map所以需要传递一个参数,调用map后会返回一个函子
* 所以在调用join
*/
flatMap(fn){
return this.map(fn).join()
}
}
function readFile(filename){
/**
* 直接读取文件会有副作用让函数变得不纯,
* 所以不直接读取文件而是返回一个IO函子,
* 也就是让读取文件延迟去执行
*/
return new IO(function() {
return fs.readFileSync(filename,'utf-8')
})
}
function print(x){
return new IO(function(){
console.log(x);
return x
})
}
// 当我们要合并的这个函数它返回的是一个值就调用map,当返回的是函子就调用flatMap
const f = readFile('package.json')
// .map(fp.toUpper)
.map(x => x.toUpperCase())
.flatMap(print)
.join();
console.log(f);
在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有等主线程任务执行完毕,"任务队列"开始通知主线程,请求执行任务,该任务才会进入主线程执行。
不会等待这个任务的结束才开始下一个任务,开启过后就立即执行下一个任务,后续逻辑一般会通过回调函数的方式定义,没有异步模式JavaScript语言就无法同时处理大量耗时任务
一个函数被作为参数传递给另一个函数(在这里我们把另一个函数叫做“otherFunction”),回调函数在otherFunction中被调用。
/注意到click方法中是一个函数而不是一个变量
//它就是回调函数
$("#btn_1").click(function() {
alert("Btn 1 Clicked");
});
//或者
function click() { // 它就是回调函数
alert("Btn 1 Clicked");
}
$("#btn_1").click(click);
Promise 对象用于表示一个异步操作的最终完成或失败,及其结果值。
Promise 构造函数接收一个函数executor为参数,这个函数有又接收两个函数作为参数 resolve 和 reject。Promise构造函数执行时立即调用executor 函数, resolve 和 reject 两个函数作为参数传递给executor(executor 函数在Promise构造函数返回所建promise实例对象前被调用)。resolve 和 reject 函数被调用时,分别将promise的状态改为fulfilled(完成)或rejected(失败)。executor 内部通常会执行一些异步操作,一旦异步操作执行完毕(可能成功/失败),要么调用resolve函数来将promise状态改成fulfilled,要么调用reject 函数将promise的状态改为rejected。如果在executor函数中抛出一个错误,那么该promise 状态为rejected。executor函数的返回值被忽略。
一个Promise 有以下几种状态:
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。
// 打印順序是111 222 333,說明 111函數內部是個同步的,它的異步核心是resolve和reject函數
const promise = new Promise((resolve,reject)=>{
console.log(111);
// 成功的承諾
// resolve(333)
// 失敗的承諾
reject(new Error('失敗'))
})
promise.then((data)=> {
console.log(data);
console.log(333);
},(err) => {
console.log(err);
})
console.log(222);
// ajax案例
function ajax(url){
return new Promise(function(resolve,reject) {
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json';
xhr.onload = function(){
if(this.status === 200){
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send();
})
}
ajax('./datas.json').then(function(data) {
// 请求成功
console.log(data);
},function(err) {
// 请求失败
console.log(err);
})
本质是通过回调函数的定义的异步任务结束后所需执行的任务,通过then方法传递进去的。
嵌套使用的方式是使用Promise最常见的错误
// ajax案例
function ajax(url){
return new Promise(function(resolve,reject) {
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json';
xhr.onload = function(){
if(this.status === 200){
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send();
})
}
ajax('./datas.json').then(function(data) {
// 请求成功
return data
}).then(function(data) {
// 前面then方法中回调函数的返回值会作为后面then方法回调的参数
return 'foo'
// 可以省略不写
},function(err) {
}).then(function(data) {
console.log(data); //foo
})
function ajax(url){
return new Promise(function(resolve,reject) {
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json';
xhr.onload = function(){
if(this.status === 200){
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send();
})
}
// 不推荐
// ajax('./datas.json').then(function(data) {
// // 这里返回一个失败的
// return ajax('./datas11.json')
// },function(err) {
// console.log(err);
// // 这里并没有捕获到异常而是下一个then里面的失败回调函数才能捕获到
// }).then(function(data) {
// console.log(222);
// },function(err) {
// // 这里才能捕获到那个失败的异常
// console.log(err);
// })
// 推荐使用
ajax('./dat11as.json').then(function(data) {
// 请求成功
return data
}).then(function(data) {
console.log(222);
// 因为Promise是每次都是返回一个新的Promise对象,
// 所以catch也只是对前面then方法返回的Promise指定失败的回调
// 只不过它们都是同一个Promise链上,所以只要前面有一个异常那么就会一直被传递,
// 所以在这里就能捕获到异常
}).catch(function(err) {
// 这里能捕获到异常
console.log(err);
})
function ajax(url){
return new Promise(function(resolve,reject) {
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json';
xhr.onload = function(){
if(this.status === 200){
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send();
})
}
// Promise.resolve() 就是一定会返回一个成功的回调
Promise.resolve('foo')
// 上面代码等同于
new Promise(function(resolve, reject) {
resolve('foo')
})
// 如果Promise.resolve()传入的是个Promise对象,那么他会直接返回传入的那个对象
const promise = ajax('./datas.json')
const promise2 = Promise.resolve(promise)
console.log(promise === promise2); // true
// Promise.reject() 就是一定会返回一个失败的回调
Promise.reject('anythig')
.catch(function(error) {
console.log(error); // anythig
})
function ajax(url){
return new Promise(function(resolve,reject) {
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json';
xhr.onload = function(){
if(this.status === 200){
resolve(this.response)
} else {
reject(new Error(this.statusText))
}
}
xhr.send();
})
}
// Promise.all
const promise = ajax('./urls.json').then(function(data) {
// console.log(data);
// 返回一个数组,数组的每一项是传入的这个对象的每一个键值对的值
const urls = Object.values(data);
// 把字符串数组转成每一项都是Promise对象的数组
const tasks = urls.map(item => ajax(item))
// console.log(tasks);
return Promise.all(tasks)
})
.then(function(data) {
// 拿到当前Promise.all中每一个异步请求的结果的数据
console.log(data);
})
// Promise.race
// 这是能成功执行的promise函数
const promise = ajax('./urls.json');
// 这个1秒后执行的失败的promise函数
const reject = new Promise(function(resolve,reject) {
setTimeout(() => {
reject(new Error('timeout'))
}, 1000);
})
// 只要有一个结束,就会race就会调用对应的函数。可以用作ajax请求超时
// 例如这里如果promise这个函数没在1秒前执行完的话,那么就会执行reject,
// 然后race的catch就会捕捉到失败的异常.
Promise.race([promise,reject]).then(val => {
console.log(val);
}).catch(err => {
console.log(err);
})
优先级:同步代码 > 微任务(Promise、MutationObserver以及nodejs中的process.nextTick) > 宏任务(setTimeout以及大部分异步调用的api)
微任务的作用是提高整体的相应能力。
function* test() {
console.log(444);
const cs1s = yield 'foo';
console.log(cs1s); // pppppp
// try {
// console.log(444);
// const cs1s = yield 'foo';
// console.log(cs1s); // pppppp
// } catch (error) {
// console.log(error);
// }
}
// 调用并不会立即执行,而只是生成了一个Generator空对象.
const Generator = test(); // Object [Generator] {}
/**
* 只有调用Generator的next方法时才会执行并返回一个对象{value: undefined, done: true}
* yield表示暂停并把返回值赋值给对象的value属性 return表示结束并把返回值赋值给对象的value属性
* done表示代码是否全部执行完只有使用yield是done为false。
* 下次调用next时从上次暂停处继续执行,直到下一处yield或return。
* next方法接收一个参数,作为yield的返回值
*/
const aa = Generator.next();
console.log(aa); // {value: "foo", done: false}
const bb = Generator.next('pppppp');
console.log(bb); // {value: "foo1111", done: false}
// 抛出一个错误可以在test内部捕获错误
Generator.throw('error111');
function ajax(url){
return new Promise(function(resolve,reject){
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json';
xhr.onload = function(){
if (this.status === 200){
resolve(this.response)
} else {
reject(this.statusText)
}
}
xhr.send()
})
}
function * generat(){
console.log('start');
const data = yield ajax('./data.json');
console.log(data);
const data1 = yield ajax('./data1.json');
console.log(data1);
}
const generator = generat();
const promise1 = generator.next();
promise1.value.then(data => {
if (promise1.done) return
const promise2 = generator.next(data);
promise2.value.then(data1 => {
const promise3 = generator.next(data1);
})
})
// const promise1 = generator.next();
// promise1.value.then(data => {
// console.log(data);
// if (promise1.done) return
// })
// const promise2 = generator.next();
// promise2.value.then(data => {
// console.log(data);
// if (promise2.done) return
// })
这样异步函数就跟写同步函数很像了,但是他还是比较麻烦,因为要自己去创建一个生成器函数
function ajax(url){
return new Promise(function(resolve,reject){
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json';
xhr.onload = function(){
if (this.status === 200){
resolve(this.response)
} else {
reject(this.statusText)
}
}
xhr.send()
})
}
function * generat(){
console.log('start');
const data = yield ajax('./data.json');
console.log(data);
const data1 = yield ajax('./data1.json');
console.log(data1);
}
// 使用递归和生成器函数的方法处理异步函数
// const generator = generat();
// function handleResult (promise1){
// if (promise1.done) return // 生成器函数结束
// promise1.value.then(data => {
// handleResult(generator.next(data))
// }, err => {
// console.log(err);
// })
// }
// handleResult(generator.next())
// 把使用递归和生成器函数进行封装
function co (generat){
const generator = generat();
function handleResult (promise1){
if (promise1.done) return // 生成器函数结束
promise1.value.then(data => {
handleResult(generator.next(data))
}, err => {
console.log(err);
})
}
handleResult(generator.next())
}
co(generat)
function ajax(url){
return new Promise(function(resolve,reject){
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.responseType = 'json';
xhr.onload = function(){
if (this.status === 200){
resolve(this.response)
} else {
reject(this.statusText)
}
}
xhr.send()
})
}
// async/await
async function generat(){
console.log('start');
const data = await ajax('./data.json');
console.log(data);
const data1 = await ajax('./data1.json');
console.log(data1);
}
generat()
// async/await 和generator用法上很像
// function * generat(){
// console.log('start');
// const data = yield ajax('./data.json');
// console.log(data);
// const data1 = yield ajax('./data1.json');
// console.log(data1);
// }
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 构造函数
constructor(executor) {
// 传入的执行器函数,有两个函数作为参数并立即执行
executor(this.resolve,this.reject);
}
// promise状态,默认为 pending
status = PENDING;
// 成功之后的值,默认 undefined
value = undefined;
// 失败后的值,默认 undefined
reason = undefined;
// 成功的函数
// 因为调用是直接调用的即resolve()所以要使用箭头函数,它的this才是指向这个类的实例对象也就是promise对象
resolve = (value) => {
// 如果状态等于 pending 就不执行
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
}
// 失败的函数
reject = (reason) => {
// 如果状态等于 pending 就不执行
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
}
// then不是直接调用的,所以不需要使用箭头函数
then(successfulCallBack, loseCallBack) {
// 成功后调用成功的回调,并返回成功的数据
if (this.status === FULFILLED) {
successfulCallBack(this.value);
// 失败后调用失败的回调,并返回失败的原因
} else if (this.status === REJECTED) {
loseCallBack(this.reason);
}
}
// then = (successfulCallBack, loseCallBack) => {
// // 成功后调用成功的回调,并返回成功的数据
// if (this.status === FULFILLED) {
// successfulCallBack(this.value);
// // 失败后调用失败的回调,并返回失败的原因
// } else if (this.status === REJECTED) {
// loseCallBack(this.reason);
// }
// }
}
module.exports = MyPromise
test.js
/**
* Promise做了哪些事?
* Promise其实是个类。首先传入了一个称之为执行器函数,这个执行器是立即执行的,他接受两个函数做参数第一个是成功时的函数,另一个是失败时的函数。
* 它有三个状态 pending fulfilled rejected
* pending:表示进行中的状态
* fulfilled:表示成功后的状态
* rejected:表示失败后的状态
* 状态只能如下改变
* pending ===》 fulfilled 或 pending ===》 rejected
* then方法接收两个回调函数,第一个是成功的回调函数,第二个是失败的回调函数
*/
// Promise 类核心逻辑实现
const MyPromise = require('./myPromise')
new MyPromise((resolve,reject) =>{
resolve('成功');
// reject('失败');
}).then(data => {
console.log(data); // 成功
console.log(111);
}, err => {
console.log(err); // 失败
console.log(222);
})
// 原生promise
// const promise = new Promise((resolve,reject) =>{
// // resolve('成功');
// reject('失败');
// }).then(data => {
// console.log(data);
// }, err => {
// console.log(err);
// })
test.js
/**
* 上一节只是同步代码的执行而没考虑到异步代码的执行,这次考虑异步
*/
// Promise 实现异步代码调用
const MyPromise = require('./myPromise')
new MyPromise((resolve,reject) =>{
// 这里使用了异步代码,不会马上执行,而是放到事件队列中去,但是下面的then会马上执行
setTimeout(() => {
// resolve('成功');
reject('失败');
}, 2000);
}).then(data => {
console.log(data); // 成功
}, err => {
console.log(err); // 失败
})
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
// 构造函数
constructor(executor) {
// 传入的执行器函数,有两个函数作为参数并立即执行
executor(this.resolve,this.reject);
}
// promise状态,默认为 pending
status = PENDING;
// 成功之后的值,默认 undefined
value = undefined;
// 失败后的值,默认 undefined
reason = undefined;
// 成功后的回调
successfulCallBack = undefined;
// 失败后的回调
loseCallBack = undefined;
// 成功的函数
// 因为调用是直接调用的即resolve()所以要使用箭头函数,它的this才是指向这个类的实例对象也就是promise对象
resolve = (value) => {
// 如果状态等于 pending 就不执行
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
// 如果有值就执行这个成功的回调函数
if (this.successfulCallBack) this.successfulCallBack(this.value);
}
// 失败的函数
reject = (reason) => {
// 如果状态等于 pending 就不执行
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
// 如果有值就执行这个失败的回调函数
if (this.loseCallBack) this.loseCallBack(this.reason);
}
// then不是直接调用的,所以不需要使用箭头函数
then(successfulCallBack, loseCallBack) {
// 成功后调用成功的回调,并返回成功的数据
if (this.status === FULFILLED) {
successfulCallBack(this.value);
// 失败后调用失败的回调,并返回失败的原因
} else if (this.status === REJECTED) {
loseCallBack(this.reason);
// 异步操作
// 因为是异步操作所以当状态不确定前不知道该调用成功的还是失败的回调
// 所以先把它保存到这个类的实例属性中,等确定了状态后再调用
} else {
this.successfulCallBack = successfulCallBack;
this.loseCallBack = loseCallBack;
}
}
// then = (successfulCallBack, loseCallBack) => {
// // 成功后调用成功的回调,并返回成功的数据
// if (this.status === FULFILLED) {
// successfulCallBack(this.value);
// // 失败后调用失败的回调,并返回失败的原因
// } else if (this.status === REJECTED) {
// loseCallBack(this.reason);
// }
// }
}
module.exports = MyPromise
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
executor(this.resolve,this.reject);
}
status = PENDING;
value = undefined;
reason = undefined;
// 默认为空数组,因为有多个异步函数调用
successfulCallBack = [];
loseCallBack = [];
resolve = (value) => {
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
// 当数组的长度等于0时停止循环。 shift会改变原数组,移除数组第一项并返回
while (this.successfulCallBack.length) this.successfulCallBack.shift()(this.value);
}
reject = (reason) => {
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
// 当数组的长度等于0时停止循环。 shift会改变原数组,移除数组第一项并返回
while (this.loseCallBack.length) this.loseCallBack.shift()(this.reason);
}
then(successfulCallBack, loseCallBack) {
if (this.status === FULFILLED) {
successfulCallBack(this.value);
} else if (this.status === REJECTED) {
loseCallBack(this.reason);
// 异步操作
} else {
// 把调用then传过来的回调函数都添加到数组中去,等到状态确定后在循环调用
this.successfulCallBack.push(successfulCallBack);
this.loseCallBack.push(loseCallBack);
}
}
}
module.exports = MyPromise
test.js
/**
* 上一节虽然考虑到了异步函数,但是没考虑到多个调用
*/
// Promise 实现多个异步代码调用
const MyPromise = require('./myPromise')
const promise = new MyPromise((resolve,reject) =>{
// 这里使用了异步代码,不会马上执行,而是放到事件队列中去,但是下面的then会马上执行
setTimeout(() => {
resolve('成功');
// reject('失败');
}, 2000);
})
promise.then(data => {
console.log(data); // 成功
}, err => {
console.log(err); // 失败
})
promise.then(data => {
console.log(data); // 成功
}, err => {
console.log(err); // 失败
})
promise.then(data => {
console.log(data); // 成功
}, err => {
console.log(err); // 失败
})
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
executor(this.resolve,this.reject);
}
status = PENDING;
value = undefined;
reason = undefined;
successfulCallBack = [];
loseCallBack = [];
resolve = (value) => {
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
while (this.successfulCallBack.length) this.successfulCallBack.shift()(this.value);
}
reject = (reason) => {
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
while (this.loseCallBack.length) this.loseCallBack.shift()(this.reason);
}
then(successfulCallBack, loseCallBack) {
// 1.then方法要链式调用就必须要返回Promise对象,Promise接收一个执行器,这个执行器函数会立即执行
const promise2 = new Promise((resolve,reject) => {
if (this.status === FULFILLED) {
// 3.又因为我们需要把上一个then方法返回的值传递给下一个then,这里的x就是上一个的返回值
let x = successfulCallBack(this.value);
resolve(x)
// 4.既然要传递给下一个then那就调用resolve成功时的函数传递给下一个then了
} else if (this.status === REJECTED) {
let x = loseCallBack(this.reason);
reject(x)
// 异步操作
} else {
this.successfulCallBack.push(successfulCallBack);
this.loseCallBack.push(loseCallBack);
}
});
// 2.而这一段代码也是需要立即执行的,所以在创建一个新的Promise的时候需要把这一段代码放进去
// if (this.status === FULFILLED) {
// successfulCallBack(this.value);
// } else if (this.status === REJECTED) {
// loseCallBack(this.reason);
// // 异步操作
// } else {
// this.successfulCallBack.push(successfulCallBack);
// this.loseCallBack.push(loseCallBack);
// }
// 返回Promise
return promise2
}
}
module.exports = MyPromise
test.js
/**
* 上一节虽然考虑到了异步函数,但是没考虑到多个调用
*/
// Promise 实现多个异步代码调用
const MyPromise = require('./myPromise')
const promise = new MyPromise((resolve,reject) =>{
// 这里使用了异步代码,不会马上执行,而是放到事件队列中去,但是下面的then会马上执行
// setTimeout(() => {
// resolve('成功');
// reject('失败');
// }, 2000);
resolve('成功');
})
promise.then(data => {
console.log(data); // 成功
return 100;
}).then(data => {
console.log(data); // 100promise.then(data => {
console.log(data); // 成功
return 100;
},err => {
console.log(4444);
return 'shibai'
}).then(data => {
console.log(data); // 100
}, err => {
console.log(err);
})
})
// 1.then方法要链式调用就必须要返回Promise对象
// 2.需要把上一个then的返回值传递给下一个then成功的回调
test.js
/**
* 上一节虽然考虑到了异步代码的多个调用,但是没考虑到then方法的返回值得问题
* 如果then返回Promise对象则调用相应回调,即这个Promise对象的状态是成功就调用成功的回调,状态是失败就调用失败的回调
* 如果返回的不是Promise对象则调用对应的resolve或reject返回给下一个then
*/
const MyPromise = require('./myPromise')
const promise = new MyPromise((resolve,reject) =>{
// 这里使用了异步代码,不会马上执行,而是放到事件队列中去,但是下面的then会马上执行
// setTimeout(() => {
// resolve('成功');
// reject('失败');
// }, 2000);
resolve('成功');
// reject('失败');
})
promise.then(data => {
console.log(data,'pppp'); // 成功
return other();
}).then(data => {
console.log(data, '******'); // other
}).then(data => {
// 这里打印undefined是因为上一个then没有返回值
console.log(data, '---------'); // undefined
})
function other() {
return new MyPromise((resolve, reject) => {
resolve('other');
})
}
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
executor(this.resolve,this.reject);
}
status = PENDING;
value = undefined;
reason = undefined;
successfulCallBack = [];
loseCallBack = [];
resolve = (value) => {
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
while (this.successfulCallBack.length) this.successfulCallBack.shift()(this.value);
}
reject = (reason) => {
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
while (this.loseCallBack.length) this.loseCallBack.shift()(this.reason);
}
then(successfulCallBack, loseCallBack) {
// 1.then方法要链式调用就必须要返回Promise对象,Promise接收一个执行器,这个执行器函数会立即执行
const promise2 = new Promise((resolve,reject) => {
if (this.status === FULFILLED) {
// 3.又因为我们需要把上一个then方法返回的值传递给下一个then,这里的x就是上一个的返回值
let x = successfulCallBack(this.value);
/**
* 如果then返回Promise对象则调用相应回调,即这个Promise对象的状态是成功就调用成功的回调,状态是失败就调用失败的回调
* 如果返回的不是Promise对象则调用对应的resolve或reject返回给下一个then
*/
resolvePromise(x,resolve,reject)
// resolve(x)
// 4.既然要传递给下一个then那就调用resolve成功时的函数传递给下一个then了
} else if (this.status === REJECTED) {
let x = loseCallBack(this.reason);
reject(x)
// 异步操作
} else {
this.successfulCallBack.push(successfulCallBack);
this.loseCallBack.push(loseCallBack);
}
});
return promise2
}
}
function resolvePromise(x,resolve,reject) {
// 是Promise对象 instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
if (x instanceof MyPromise) {
// 调用返回的这个promise的then方法去查看这个promise的状态,如果成功的状态则调用resolve,失败就调用reject
// x.then((value) => resolve(value), (err) => reject(err))
// 简写
x.then(resolve,reject);
} else {
// 如果不是Promise对象则直接调用
resolve(x)
}
}
module.exports = MyPromise
test.js
const MyPromise = require('./myPromise')
// 原生Promise中如果调用then方法后再返回自己,则会报错 Chaining cycle detected for promise #
// const promise = new Promise(function(resolve,reject) {
// resolve('dd')
// })
// var p1 = promise.then(data => {
// return p1
// }, err => {
// // console.log(err);
// })
// console.log(p1,'xxxxx');
// 自己实现的捕获调用then方法后再返回自己的错误
const promise1 = new MyPromise(function(resolve,reject) {
resolve('dd')
})
// 这里调用Promise的then方法后又返回了自己p1
var p1 = promise1.then(data => {
return p1
}, err => {
console.log(err.message);
})
// console.log(p1,'xxxxx');
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
executor(this.resolve,this.reject);
}
status = PENDING;
value = undefined;
reason = undefined;
successfulCallBack = [];
loseCallBack = [];
resolve = (value) => {
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
while (this.successfulCallBack.length) this.successfulCallBack.shift()(this.value);
}
reject = (reason) => {
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
while (this.loseCallBack.length) this.loseCallBack.shift()(this.reason);
}
then(successfulCallBack, loseCallBack) {
// promise2就是test.js中的var p1中的那个Promise对象,而这里的x就是test.js中return返回的那个值
// 所以只要对比下就知道它们是不是同一个Promise是的话就直接报错,把判断逻辑放到resolvePromise中,因为这个很多地方需要用到
const promise2 = new Promise((resolve,reject) => {
if (this.status === FULFILLED) {
// 错误代码。但是在new Promise是不能直接拿到promise2的值的,因为promise2是new Promise执行完的结果
// 要想拿到就可以使用异步调用,等new Promise执行完后在取就可以了
// let x = successfulCallBack(this.value);
// resolvePromise(promise2,x,resolve,reject)
setTimeout(() => {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
}, 0);
} else if (this.status === REJECTED) {
let x = loseCallBack(this.reason);
reject(x)
// 异步操作
} else {
this.successfulCallBack.push(successfulCallBack);
this.loseCallBack.push(loseCallBack);
}
});
return promise2
}
}
/**
* 如果then返回Promise对象则调用相应回调,即这个Promise对象的状态是成功就调用成功的回调,状态是失败就调用失败的回调
* 如果返回的不是Promise对象则调用对应的resolve或reject返回给下一个then
*/
function resolvePromise(promise2,x,resolve,reject) {
if (promise2 === x) {
reject(new TypeError("Chaining cycle detected for promise #"))
}
// 是Promise对象 instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
if (x instanceof MyPromise) {
// 调用返回的这个promise的then方法去查看这个promise的状态,如果成功的状态则调用resolve,失败就调用reject
// x.then((value) => resolve(value), (err) => reject(err))
// 简写
x.then(resolve,reject);
} else {
// 如果不是Promise对象则直接调用
resolve(x)
}
}
module.exports = MyPromise
test.js
const MyPromise = require('./myPromise')
// const promise1 = new MyPromise(function(resolve,reject) {
// // 1.执行器中手动抛出一个错误
// throw new Error('shib111')
// resolve('dd')
// })
// var p1 = promise1.then(data => {
// // 2.在成功函数的抛出错误,会在下一个then方法的失败回调中报错错误
// // throw new Error('成功回调抛出的错误')
// resolve('dd')
// }, err => {
// console.log(err,'llllll'); // Error: shib111
// })
// .then(data => {
// console.log(data);
// }, err => {
// console.log(err,'ppppp'); // Error: 成功回调抛出的错误
// })
const promise2 = new MyPromise(function(resolve,reject) {
// 3.执行器中异步操作手动抛出一个错误
setTimeout(() => {
resolve('成功');
}, 2000);
})
promise2.then(data => {
console.log(data,'[[['); // 成功
return 'aaaa'
},err => {
console.log(err,'ppppp');
return 10000
}).then(data => {
console.log(data,'sss'); // aaaa
}, err => {
console.log(err,'lll');
})
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 1.捕捉执行器抛出的错误
try {
executor(this.resolve,this.reject);
} catch (error) {
this.reject(error);
}
}
status = PENDING;
value = undefined;
reason = undefined;
successfulCallBack = [];
loseCallBack = [];
resolve = (value) => {
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
while (this.successfulCallBack.length) this.successfulCallBack.shift()();
}
reject = (reason) => {
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
while (this.loseCallBack.length) this.loseCallBack.shift()();
}
then(successfulCallBack, loseCallBack) {
const promise2 = new Promise((resolve,reject) => {
if (this.status === FULFILLED) {
// 2.捕捉成功函数的错误
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
} else if (this.status === REJECTED) {
let x = loseCallBack(this.reason);
reject(x)
// 3.捕捉失败函数的错误
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
// 异步操作
} else {
// 4.在异步操作中捕获错误
// 未捕获前的代码
// this.successfulCallBack.push(successfulCallBack);
// this.loseCallBack.push(loseCallBack);
// 修改后的代码
// 直接传递一个参数无法处理数据,所以返回一个函数
this.successfulCallBack.push(() => {
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
this.loseCallBack.push(() => {
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2
}
}
/**
* 如果then返回Promise对象则调用相应回调,即这个Promise对象的状态是成功就调用成功的回调,状态是失败就调用失败的回调
* 如果返回的不是Promise对象则调用对应的resolve或reject返回给下一个then
*/
function resolvePromise(promise2,x,resolve,reject) {
if (promise2 === x) {
reject(new TypeError("Chaining cycle detected for promise #"))
}
// 是Promise对象 instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
if (x instanceof MyPromise) {
// 调用返回的这个promise的then方法去查看这个promise的状态,如果成功的状态则调用resolve,失败就调用reject
// x.then((value) => resolve(value), (err) => reject(err))
// 简写
x.then(resolve,reject);
} else {
// 如果不是Promise对象则直接调用
resolve(x)
}
}
module.exports = MyPromise
test.js
/**
* 将then的参数变成可选参数
*/
// 原生Promise中then方法不传递也会返回调用resolve或reject时返回的值
const promise = new Promise(function(resolve,reject) {
resolve('成功')
})
promise.then()
.then()
.then(data => {
console.log(data); // 成功
})
// 自己实现这么效果
const MyPromise = require('./myPromise')
const p1 = new MyPromise(function(reslove,reject) {
// reslove('成功了')
reject('失败了')
})
p1.then()
.then()
.then(data => {
console.log(data); // 成功了
},err => {
console.log(err); // 失败了
})
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 1.捕捉执行器抛出的错误
try {
executor(this.resolve,this.reject);
} catch (error) {
this.reject(error);
}
}
status = PENDING;
value = undefined;
reason = undefined;
successfulCallBack = [];
loseCallBack = [];
resolve = (value) => {
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
while (this.successfulCallBack.length) this.successfulCallBack.shift()();
}
reject = (reason) => {
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
while (this.loseCallBack.length) this.loseCallBack.shift()();
}
then(successfulCallBack, loseCallBack) {
// 当调用then方法时没有传入参数那么我们就默认给他一个函数作为参数,这个函数就把resolve或resolve传入的结果直接返回给下一个then
successfulCallBack = successfulCallBack ? successfulCallBack : (val) => val
loseCallBack = loseCallBack ? loseCallBack : (rea) => rea
const promise2 = new Promise((resolve,reject) => {
if (this.status === FULFILLED) {
// 2.捕捉成功函数的错误
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
} else if (this.status === REJECTED) {
let x = loseCallBack(this.reason);
reject(x)
// 3.捕捉失败函数的错误
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
// 异步操作
} else {
// 4.在异步操作中捕获错误
// 未捕获前的代码
// this.successfulCallBack.push(successfulCallBack);
// this.loseCallBack.push(loseCallBack);
// 修改后的代码
// 直接传递一个参数无法处理数据,所以返回一个函数
this.successfulCallBack.push(() => {
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
this.loseCallBack.push(() => {
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2
}
}
/**
* 如果then返回Promise对象则调用相应回调,即这个Promise对象的状态是成功就调用成功的回调,状态是失败就调用失败的回调
* 如果返回的不是Promise对象则调用对应的resolve或reject返回给下一个then
*/
function resolvePromise(promise2,x,resolve,reject) {
if (promise2 === x) {
reject(new TypeError("Chaining cycle detected for promise #"))
}
// 是Promise对象 instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
if (x instanceof MyPromise) {
// 调用返回的这个promise的then方法去查看这个promise的状态,如果成功的状态则调用resolve,失败就调用reject
// x.then((value) => resolve(value), (err) => reject(err))
// 简写
x.then(resolve,reject);
} else {
// 如果不是Promise对象则直接调用
resolve(x)
}
}
module.exports = MyPromise
test.js
/**
* all可以让异步代码调用的顺序得到异步代码执行得到的结果
* 利用类点一个方法名则一定是个静态方法例如promise.then中的promise是类Promise的实例
* 而all方法的调用是Promise.all直接用类进行调用,所以all方法就是一个静态的方法
* all返回一个promise对象
* all接收一个数组,这个数组每一项可以是promise对象或普通值
* all方法会按这个数组参数来依次执行并依次返回执行结果,不管它是不是异步调用
* all方法只要有一个调用失败即停止执行返回失败状态
*/
// 自己实现这么效果
const MyPromise = require('./myPromise')
const p1 = new MyPromise(function(reslove,reject) {
setTimeout(() => {
reslove('成功了')
}, 2000);
})
const p2 = new MyPromise(function(reslove,reject) {
reslove('成功了哦哦哦哦哦哦')
})
MyPromise.all(['1','5',p1,'8',p2])
.then(value => {
// 打印结果是[ '1', '5', <1 empty item>, '8', '成功了哦哦哦哦哦哦' ]
// <1 empty item>原本应该是p1和p2的返回值,但是却返回的是空的
// 原因就是myPromise.js中的静态方法all中有说明
console.log(value); // 正确结果是[ '1', '5', '成功了', '8', '成功了哦哦哦哦哦哦' ]
},err => {
console.log(err);
})
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 1.捕捉执行器抛出的错误
try {
executor(this.resolve,this.reject);
} catch (error) {
this.reject(error);
}
}
status = PENDING;
value = undefined;
reason = undefined;
successfulCallBack = [];
loseCallBack = [];
resolve = (value) => {
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
while (this.successfulCallBack.length) this.successfulCallBack.shift()();
}
reject = (reason) => {
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
while (this.loseCallBack.length) this.loseCallBack.shift()();
}
static all(array) {
// 返回的数组
let result = [];
// 用于判断何时执行addData中resolve的调用时机
let index = 0;
return new MyPromise(function(resolve,reject) {
// 传入索引是为了返回的结果也是按原顺序返回
function addData(key, val) {
index ++;
result[key] = val;
if (index === array.length) {
resolve(result);
}
}
for (let i = 0; i < array.length; i++) {
const element = array[i];
if (element instanceof MyPromise) {
// 是promise对象,需要调用这个promise的then方法查看他的状态,成功状态则继续添加进要返回的数组中,失败的状态就直接调用reject返回错误信息
element.then(value => {
addData(i,value)
}, reason => reject(reason))
} else {
// 不是promise对象则直接将结果添加到返回的那个数组中
addData(i,element)
}
}
// 因为for循环是同步代码,而array中的每一项有可能是一个异步执行的代码,所以for循环执行完,但是因为其中有些项是异步所以会先会继续执行resolve方法,
// 所以此时result是未添加异步代码执行后返回的结果的,可以把它写在addData中因为addData就是在异步中调用的,
// 但是这样的话每循环依次就会执行一次resolve是不行的,所以加个index作为标识
//
// resolve(result)
})
}
then(successfulCallBack, loseCallBack) {
// 当调用then方法时没有传入参数那么我们就默认给他一个函数作为参数,这个函数就把resolve或resolve传入的结果直接返回给下一个then
successfulCallBack = successfulCallBack ? successfulCallBack : (val) => val
loseCallBack = loseCallBack ? loseCallBack : (rea) => rea
const promise2 = new Promise((resolve,reject) => {
if (this.status === FULFILLED) {
// 2.捕捉成功函数的错误
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
} else if (this.status === REJECTED) {
let x = loseCallBack(this.reason);
reject(x)
// 3.捕捉失败函数的错误
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
// 异步操作
} else {
// 4.在异步操作中捕获错误
// 未捕获前的代码
// this.successfulCallBack.push(successfulCallBack);
// this.loseCallBack.push(loseCallBack);
// 修改后的代码
// 直接传递一个参数无法处理数据,所以返回一个函数
this.successfulCallBack.push(() => {
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
this.loseCallBack.push(() => {
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2
}
}
/**
* 如果then返回Promise对象则调用相应回调,即这个Promise对象的状态是成功就调用成功的回调,状态是失败就调用失败的回调
* 如果返回的不是Promise对象则调用对应的resolve或reject返回给下一个then
*/
function resolvePromise(promise2,x,resolve,reject) {
if (promise2 === x) {
reject(new TypeError("Chaining cycle detected for promise #"))
}
// 是Promise对象 instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
if (x instanceof MyPromise) {
// 调用返回的这个promise的then方法去查看这个promise的状态,如果成功的状态则调用resolve,失败就调用reject
// x.then((value) => resolve(value), (err) => reject(err))
// 简写
x.then(resolve,reject);
} else {
// 如果不是Promise对象则直接调用
resolve(x)
}
}
module.exports = MyPromise
test.js
/**
* Promise.resolve把传过去的值变成promise对象并返回
* Promise.resolve方法接收一个参数,如果这个参数不是promise对象的话就会包装promise对象返回,如果参数是promise对象的话直接返回,
*/
// 原生Promise
Promise.resolve(1000).then(value => console.log(value))
// 自己实现这个效果
// 是类直接调用所以是静态方法
const MyPromise = require('./myPromise')
MyPromise.resolve(2000).then(value => console.log(value))
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 1.捕捉执行器抛出的错误
try {
executor(this.resolve,this.reject);
} catch (error) {
this.reject(error);
}
}
status = PENDING;
value = undefined;
reason = undefined;
successfulCallBack = [];
loseCallBack = [];
resolve = (value) => {
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
while (this.successfulCallBack.length) this.successfulCallBack.shift()();
}
reject = (reason) => {
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
while (this.loseCallBack.length) this.loseCallBack.shift()();
}
static resolve(val) {
// 如果是MyPromise的实例对象就说明是promise对象直接返回
if (val instanceof MyPromise) return val
// 不是返回一个promise对象并把传过来的值通过reslove返回出去
return new MyPromise(function(reslove,reject) {
reslove(val)
})
}
static all(array) {
// 返回的数组
let result = [];
// 用于判断何时执行addData中resolve的调用时机
let index = 0;
return new MyPromise(function(resolve,reject) {
// 传入索引是为了返回的结果也是按原顺序返回
function addData(key, val) {
index ++;
result[key] = val;
if (index === array.length) {
resolve(result);
}
}
for (let i = 0; i < array.length; i++) {
const element = array[i];
if (element instanceof MyPromise) {
// 是promise对象,需要调用这个promise的then方法查看他的状态,成功状态则继续添加进要返回的数组中,失败的状态就直接调用reject返回错误信息
element.then(value => {
addData(i,value)
}, reason => reject(reason))
} else {
// 不是promise对象则直接将结果添加到返回的那个数组中
addData(i,element)
}
}
// 因为for循环是同步代码,而array中的每一项有可能是一个异步执行的代码,所以for循环执行完,但是因为其中有些项是异步所以会先会继续执行resolve方法,
// 所以此时result是未添加异步代码执行后返回的结果的,可以把它写在addData中因为addData就是在异步中调用的,
// 但是这样的话每循环依次就会执行一次resolve是不行的,所以加个index作为标识
//
// resolve(result)
})
}
then(successfulCallBack, loseCallBack) {
// 当调用then方法时没有传入参数那么我们就默认给他一个函数作为参数,这个函数就把resolve或resolve传入的结果直接返回给下一个then
successfulCallBack = successfulCallBack ? successfulCallBack : (val) => val
loseCallBack = loseCallBack ? loseCallBack : (rea) => rea
const promise2 = new Promise((resolve,reject) => {
if (this.status === FULFILLED) {
// 2.捕捉成功函数的错误
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
} else if (this.status === REJECTED) {
let x = loseCallBack(this.reason);
reject(x)
// 3.捕捉失败函数的错误
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
// 异步操作
} else {
// 4.在异步操作中捕获错误
// 未捕获前的代码
// this.successfulCallBack.push(successfulCallBack);
// this.loseCallBack.push(loseCallBack);
// 修改后的代码
// 直接传递一个参数无法处理数据,所以返回一个函数
this.successfulCallBack.push(() => {
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
this.loseCallBack.push(() => {
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2
}
}
/**
* 如果then返回Promise对象则调用相应回调,即这个Promise对象的状态是成功就调用成功的回调,状态是失败就调用失败的回调
* 如果返回的不是Promise对象则调用对应的resolve或reject返回给下一个then
*/
function resolvePromise(promise2,x,resolve,reject) {
if (promise2 === x) {
reject(new TypeError("Chaining cycle detected for promise #"))
}
// 是Promise对象 instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
if (x instanceof MyPromise) {
// 调用返回的这个promise的then方法去查看这个promise的状态,如果成功的状态则调用resolve,失败就调用reject
// x.then((value) => resolve(value), (err) => reject(err))
// 简写
x.then(resolve,reject);
} else {
// 如果不是Promise对象则直接调用
resolve(x)
}
}
module.exports = MyPromise
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 1.捕捉执行器抛出的错误
try {
executor(this.resolve,this.reject);
} catch (error) {
this.reject(error);
}
}
status = PENDING;
value = undefined;
reason = undefined;
successfulCallBack = [];
loseCallBack = [];
resolve = (value) => {
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
while (this.successfulCallBack.length) this.successfulCallBack.shift()();
}
reject = (reason) => {
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
while (this.loseCallBack.length) this.loseCallBack.shift()();
}
// 不管失败和成功都会执行,因为finally后面还可以使用then方法所以要返回一个promise对象,
// 可以通过调用then而拿到返回值
finally(callback) {
// 这里返回的是一个promise对象
return this.then(value => {
// 如果test.js中的finally参数内部手动return,那他的返回值的结果就是callback() === test.js中return的那个p1
// 调用.then(() => value)是为了给finally后面的then返回值,即myPromise中reslove或reject的值,
// 而并不是return的那个p1promise中的reslove或reject的值
return MyPromise.resolve(callback()).then(() => value)
},reason => {
return MyPromise.resolve(callback()).then(() => reason)
})
}
static resolve(val) {
// 如果是MyPromise的实例对象就说明是promise对象直接返回
if (val instanceof MyPromise) return val
// 不是返回一个promise对象并把传过来的值通过reslove返回出去
return new MyPromise(function(reslove,reject) {
reslove(val)
})
}
static all(array) {
// 返回的数组
let result = [];
// 用于判断何时执行addData中resolve的调用时机
let index = 0;
return new MyPromise(function(resolve,reject) {
// 传入索引是为了返回的结果也是按原顺序返回
function addData(key, val) {
index ++;
result[key] = val;
if (index === array.length) {
resolve(result);
}
}
for (let i = 0; i < array.length; i++) {
const element = array[i];
if (element instanceof MyPromise) {
// 是promise对象,需要调用这个promise的then方法查看他的状态,成功状态则继续添加进要返回的数组中,失败的状态就直接调用reject返回错误信息
element.then(value => {
addData(i,value)
}, reason => reject(reason))
} else {
// 不是promise对象则直接将结果添加到返回的那个数组中
addData(i,element)
}
}
// 因为for循环是同步代码,而array中的每一项有可能是一个异步执行的代码,所以for循环执行完,但是因为其中有些项是异步所以会先会继续执行resolve方法,
// 所以此时result是未添加异步代码执行后返回的结果的,可以把它写在addData中因为addData就是在异步中调用的,
// 但是这样的话每循环依次就会执行一次resolve是不行的,所以加个index作为标识
//
// resolve(result)
})
}
then(successfulCallBack, loseCallBack) {
// 当调用then方法时没有传入参数那么我们就默认给他一个函数作为参数,这个函数就把resolve或resolve传入的结果直接返回给下一个then
successfulCallBack = successfulCallBack ? successfulCallBack : (val) => val
loseCallBack = loseCallBack ? loseCallBack : (rea) => rea
const promise2 = new Promise((resolve,reject) => {
if (this.status === FULFILLED) {
// 2.捕捉成功函数的错误
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
} else if (this.status === REJECTED) {
let x = loseCallBack(this.reason);
reject(x)
// 3.捕捉失败函数的错误
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
// 异步操作
} else {
// 4.在异步操作中捕获错误
// 未捕获前的代码
// this.successfulCallBack.push(successfulCallBack);
// this.loseCallBack.push(loseCallBack);
// 修改后的代码
// 直接传递一个参数无法处理数据,所以返回一个函数
this.successfulCallBack.push(() => {
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
this.loseCallBack.push(() => {
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2
}
}
/**
* 如果then返回Promise对象则调用相应回调,即这个Promise对象的状态是成功就调用成功的回调,状态是失败就调用失败的回调
* 如果返回的不是Promise对象则调用对应的resolve或reject返回给下一个then
*/
function resolvePromise(promise2,x,resolve,reject) {
if (promise2 === x) {
reject(new TypeError("Chaining cycle detected for promise #"))
}
// 是Promise对象 instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
if (x instanceof MyPromise) {
// 调用返回的这个promise的then方法去查看这个promise的状态,如果成功的状态则调用resolve,失败就调用reject
// x.then((value) => resolve(value), (err) => reject(err))
// 简写
x.then(resolve,reject);
} else {
// 如果不是Promise对象则直接调用
resolve(x)
}
}
module.exports = MyPromise
test.js
/**
* finally方法
* 无论promise执行的结果是成功还是失败,都会执行一次
* finally方法后面的then方法会接受到finally方法里面的值
* 在finally的 return返回的值并不会传递给下一个then方法的回调中去
* 在finally的 return一个异步代码需等待异步执行完后再返回
* 在then的 return返回的值会传递给下一个then方法的回调中去
*/
// 原生Promise
// const promise = new Promise(function(resolve, reject) {
// resolve('成功啦')
// // reject('失败啦')
// })
// const p1 = new Promise(function(resolve, reject) {
// setTimeout(() => {
// resolve('迟到成功啦')
// }, 2000);
// })
// promise.finally(value => {
// console.log('不管失败还是成功我都执行的呀');
// // 在finally return返回的值并不会传递给下一个then方法的回调中去
// return p1
// }).then(value => {
// console.log(value,'vallll');
// // 在then return返回的值会传递给下一个then方法的回调中去
// return 'niii'
// },err => {
// console.log(err,'errr');
// }).then(value => {
// console.log(value,'aaaaa');
// },err => {
// console.log(err,'errr');
// })
// 自己实现这个效果
// 是类直接调用所以是静态方法
const MyPromise = require('./myPromise');
const myPromise = new MyPromise(function(resolve,reject) {
resolve('是成功哦')
// reject('是失败哦')
})
// 如果在finally中返回一个异步的promise,则需要等待这个异步执行完再返回并且需要把这个传递给下个then的对应回调中
const p1 = new MyPromise(function(resolve,reject) {
setTimeout(() => {
resolve('迟到的成功哦')
}, 2000);
})
myPromise.finally(val => {
console.log('不管失败还是成功我都执行的呀....');
// 在finally return返回的值并不会传递给下一个then方法的回调中去
return p1
}).then(data => {
console.log(data); // 等待2秒后打印 是成功哦
// 在then return返回的值会传递给下一个then方法的回调中去
// return 'niii'
}, err => {
console.log(err);
})
test.js
/**
* catch方法的实现
* 当调用then方法而不传递第二个参数的话,原生promise默认是传入undefined的,那then的异常信息就可以在catch方法中捕获到
* catch内部调用的也是then方法只不过没有注册成功的回调而是注册失败的回调
*/
// 原生Promise
// const promise = new Promise(function(resolve, reject) {
// reject('失败啦')
// })
// promise
// .then(value => {
// console.log(value,'vallll');
// }).catch(err => {
// console.log(err,'aaaaa');
// })
// 自己实现这个效果
// 是类直接调用所以是静态方法
const MyPromise = require('./myPromise');
const myPromise = new MyPromise(function(resolve,reject) {
reject('是失败哦')
})
myPromise
.then(value => {
console.log(value,'vallll');
}).catch(err => {
console.log(err,'aaaaa'); // 是失败哦
})
myPromise.js
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor) {
// 1.捕捉执行器抛出的错误
try {
executor(this.resolve,this.reject);
} catch (error) {
this.reject(error);
}
}
status = PENDING;
value = undefined;
reason = undefined;
successfulCallBack = [];
loseCallBack = [];
resolve = (value) => {
if(this.status != PENDING) return
this.value = value;
this.status = FULFILLED;
while (this.successfulCallBack.length) this.successfulCallBack.shift()();
}
reject = (reason) => {
if(this.status != PENDING) return
this.reason = reason;
this.status = REJECTED;
while (this.loseCallBack.length) this.loseCallBack.shift()();
}
// 不管失败和成功都会执行,因为finally后面还可以使用then方法所以要返回一个promise对象,
// 可以通过调用then而拿到返回值
finally(callback) {
// 这里返回的是一个promise对象
return this.then(value => {
// 如果test.js中的finally参数内部手动return,那他的返回值的结果就是callback() === test.js中return的那个p1
// 调用.then(() => value)是为了给finally后面的then返回值,即myPromise中reslove或reject的值,
// 而并不是return的那个p1promise中的reslove或reject的值
return MyPromise.resolve(callback()).then(() => value)
},reason => {
return MyPromise.resolve(callback()).then(() => reason)
})
}
static resolve(val) {
// 如果是MyPromise的实例对象就说明是promise对象直接返回
if (val instanceof MyPromise) return val
// 不是返回一个promise对象并把传过来的值通过reslove返回出去
return new MyPromise(function(reslove,reject) {
reslove(val)
})
}
static all(array) {
// 返回的数组
let result = [];
// 用于判断何时执行addData中resolve的调用时机
let index = 0;
return new MyPromise(function(resolve,reject) {
// 传入索引是为了返回的结果也是按原顺序返回
function addData(key, val) {
index ++;
result[key] = val;
if (index === array.length) {
resolve(result);
}
}
for (let i = 0; i < array.length; i++) {
const element = array[i];
if (element instanceof MyPromise) {
// 是promise对象,需要调用这个promise的then方法查看他的状态,成功状态则继续添加进要返回的数组中,失败的状态就直接调用reject返回错误信息
element.then(value => {
addData(i,value)
}, reason => reject(reason))
} else {
// 不是promise对象则直接将结果添加到返回的那个数组中
addData(i,element)
}
}
// 因为for循环是同步代码,而array中的每一项有可能是一个异步执行的代码,所以for循环执行完,但是因为其中有些项是异步所以会先会继续执行resolve方法,
// 所以此时result是未添加异步代码执行后返回的结果的,可以把它写在addData中因为addData就是在异步中调用的,
// 但是这样的话每循环依次就会执行一次resolve是不行的,所以加个index作为标识
//
// resolve(result)
})
}
// catch内部调用的也是then方法只不过没有注册成功的回调而是注册失败的回调
catch(callback) {
return this.then(undefined,callback)
}
then(successfulCallBack, loseCallBack) {
// 当调用then方法时没有传入参数那么我们就默认给他一个函数作为参数,这个函数就把resolve或resolve传入的结果直接返回给下一个then
successfulCallBack = successfulCallBack ? successfulCallBack : (val) => val
loseCallBack = loseCallBack ? loseCallBack : (rea) => rea
const promise2 = new Promise((resolve,reject) => {
if (this.status === FULFILLED) {
// 2.捕捉成功函数的错误
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
} else if (this.status === REJECTED) {
let x = loseCallBack(this.reason);
reject(x)
// 3.捕捉失败函数的错误
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
// 异步操作
} else {
// 4.在异步操作中捕获错误
// 未捕获前的代码
// this.successfulCallBack.push(successfulCallBack);
// this.loseCallBack.push(loseCallBack);
// 修改后的代码
// 直接传递一个参数无法处理数据,所以返回一个函数
this.successfulCallBack.push(() => {
setTimeout(() => {
try {
let x = successfulCallBack(this.value);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
this.loseCallBack.push(() => {
setTimeout(() => {
try {
let x = loseCallBack(this.reason);
resolvePromise(promise2,x,resolve,reject)
} catch (error) {
reject(error);
}
}, 0);
});
}
});
return promise2
}
}
/**
* 如果then返回Promise对象则调用相应回调,即这个Promise对象的状态是成功就调用成功的回调,状态是失败就调用失败的回调
* 如果返回的不是Promise对象则调用对应的resolve或reject返回给下一个then
*/
function resolvePromise(promise2,x,resolve,reject) {
if (promise2 === x) {
reject(new TypeError("Chaining cycle detected for promise #"))
}
// 是Promise对象 instanceof 用于检测构造函数的 prototype 属性是否出现在某个实例对象的原型链
if (x instanceof MyPromise) {
// 调用返回的这个promise的then方法去查看这个promise的状态,如果成功的状态则调用resolve,失败就调用reject
// x.then((value) => resolve(value), (err) => reject(err))
// 简写
x.then(resolve,reject);
} else {
// 如果不是Promise对象则直接调用
resolve(x)
}
}
module.exports = MyPromise