JS - Promise

一、定义

特点

  • 只有有三种状态:padding(进行中)、fulfilled(成功)、rejected(失败)
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。状态只有从padding改变到fulfilled或者refected,两种改变。

存在的缺点

  • 无法取消Promise,一旦新建它就会立即执行,无法中途取消。

  • 如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。

  • 当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

二、基本用法

构造函数

ES6中,Promise对象是一个构造函数,用来生成Promise实例。

Promise构造函数接受一个函数作为参数,接受两个参数,分别是resolve和reject两个函数。

var p = new Promise((resolve, reject) => {})
  • resolve :将Promise的状态从padding改变成fufilled,异步操作执行成功后的回调函数;
  • reject:将Promise的状态从padding改变成rejected,异步操作执行失败后的回调函数。

then 链式调用

在Promise实例生产后,可以用then方法分别指定了fufilled状态和rejected状态的回调函数。

p.then(resolveFn, rejectFn)

第一个对应resolve的回调函数,第二个对应reject的回调函数。第二个参数是可选的,不一定要提供。

const p = new Promise((resolve, reject) => {
  console.log('new Promise()');
  resolve();
});

p.then(() => {
  console.log('resolve()');
}, () => {
  console.log('reject()');
});

catch的调用

promise.catch(
    error = > errorHandle(error)
)

其实这就是相当于promise.then(null, errorHandle).catch会捕获到整个异步操作中,或者是一系列连续的异步操作链中的出现的任何类型的错误,一旦抛出错误,则会直接转入到.catch中进行错误处理。

new Promise((resolve,reject)=> {
    throw(new Error("error"))
	resolve("resolve")
  }).then((e) => {
    console.log('resolve', e)
  }).catch((e) => {
    console.log('error', e) // error Error
  });

finally的用法

当promise的状态确定时,即无论拿到的是正常结果还是错误信息,总会执行finally。用finally可以做一些清理操作,比如发起网络请求后,可以停止loading显示。

new Promise((resolve,reject)=> {
    resolve('resolve');
  } ).then((e) => {
    console.log('resolve', e)
  }).catch((e) => {
    console.log('reject', e)
  }).finally(() => {
    console.log('finally')
  })
// resolve resolve
// finally

all

all() 接受数组作为参数。成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

p1,p2,p3都是Promise的实例对象,p要变成Resolved状态需要p1,p2,p3状态都是Resolved,如果p1,p2,p3至少有一个状态是Rejected,p的状态就变成Rejected。

p 要变成 Resolved 状态执行 then() 需要确保每个 Promise 都返回 Resolved,也就是说会等待每个 Promise 执行完毕。不过只要有个状态是 Rejected, 那么就马上执行 catch(), 不会等待其他还未有返回状态的 Promise。

Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。

var p1 = new Promise((resolve,reject)=> {
    resolve('p1 resoleve');
  })

  var p2 = new Promise((resolve,reject)=> {
    setTimeout(() => {
      resolve('p2 resoleve');
    }, 2000)
    
  })
 
 var p3 = new Promise((resolve,reject)=> {
     resolve('p3 resoleve');
	// reject('p3 reject');
  })

var p = Promise.all([p1,p2,p3]);

p.then(e => {
  console.log(e); // 2s 后按顺序输出 [ 'p1 resoleve', 'p2 resoleve', 'p3 resoleve' ]
}).catch((e) => {
    console.log(e) //  当p3 返回 Rejected 状态后,马上输出 'p3 reject'
});

race

race() 接受数组作为参数。

p1,p2,p3都是Promise的实例对象,只需要获得先返回的结果即可,在这个例子中p1,p2,p3三者的运行速度一样,所以取决于数组中第一位 p1的运行状态,p2,p3仍在继续执行,但执行结果将被丢弃。

当p1 是resolve, p的状态就是resolve,当p1 是reject, p的状态就是reject。

  var p1 = new Promise((resolve,reject)=> {
    resolve('p1');
  } )

  var p2 = new Promise((resolve,reject)=> {
    resolve('p2');
  } )
 
 var p3 = new Promise((resolve,reject)=> {
    reject('p3');
  } )

var p = Promise.race([p1,p2,p3]);

p.then((res) => {
   console.log('resolve', res) // 当 resolve 状态先返回
}).catch((err) => {
   console.log('reject', err)/ / 当 reject 状态先返回
});

三、深入了解 Promise

Promise的创建

Promise创建时,会传给promise一个称为excutor执行器的函数。这个excutor我们可以理解为生产者的生产过程函数。这个函数含有两个参数resolve和reject,这俩参数也同样是函数,用来传递异步操作的结果。语法如下:

let promise = new Promise(function(resolve, reject) {
  // executor 
})

判断一个对象是否是Promise

let promise = new Promise(function(resolve, reject) {})
console.log(promise instanceof Promise) // true
创建过程
  1. 在promise对象创建时,excutor会立即执行。
  2. 在resolve和reject是JS引擎自动创建的函数,我们无需自己创建,只需将其作为参数传入就好。
  3. 创建的promise的内部状态是个对象,初始时为:
    {
       state,  //pending
       result,  //undefined
    }
    

一旦exucutor执行完,要么产生value,要么产生error,此时会立即调用resolve(当产生value时)或者调用reject(当产生error)时,内部状态也会随之改变,如下图所示:
在这里插入图片描述
注意,当excutor里面即使调用了多个resolve和reject,其最终还是只执行一个,其他的都被忽略掉。

let promise = new Promise(function(resolve, reject) {
  resolve("done");

  reject(new Error("error")); // ignored
  setTimeout(() => resolve("error")); // ignored
});

Promise resolve()

有时需要将现有对象转为Promise对象,Promise.resolve方法就起到这个作用。

Promise.resolve('resolve')
// 相当于
new Promise(resolve => {
   resolve('resolve');
});
Promise reject()

Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。

Promise.reject('reject');
// 相当于
new Promise((resolve, reject) => {
   reject('reject');
});
var p = new Promise((resolve,reject)=> {
     reject();
  })
p.then(() => {
  console.log('1111')
}).catch(() => {
  console.log('222')
});
// '2222"

Promise链式调用

当有一系列的异步操作需要一个接一个执行时,可以使用promise的调用链。

举个例子,我们用fetch这个方法去发起网络请求,fetch()返回的是一个promise对象,那么我们可以对其连续地调用.then来进行一步步连续地异步操作。

注意:promise.then()返回的是一个新的promise对象,所以我们才可以继续对其调用.then

fetch(url)
  .then(response => response.json())
  .then(user => fetch(url)
  .then(response => response.json())
  }).catch(error => console.log(error));

promise.then( handleFunction ) 中的handleFunction可以返回立即值,也可以返回promise对象。

如果返回立即值,则可以直接把结果传入到下一步的.then进行处理,但是如果返回的是promise对象,那么一定要等到这个返回的promise处理完,拿到结果后,才会进行下一步的 then处理。

在这里插入图片描述

Promise.resoleve参数

1.参数是promise实例

直接返回这个实例本身,不做处理。

  const p = new Promise((resolve, reject) => {
    resolve("111")  
  })
  new Promise((resolve, reject) => {
    resolve(p)   
  }).then(res => {
    console.log(res)         // 111
  })

2.参数是一个thenable对象

thenable对象指的是具有then方法的对象,则会将此对象生成为一个promise,状态为resolved,并调用其then方法。比如下面这个对象。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

Promise.resolve方法会将这个对象转为 Promise 对象,然后就立即执行thenable对象的then方法。

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

thenable对象的then方法执行后,对象p1的状态就变为resolved,从而立即执行最后那个then方法指定的回调函数,输出 42。

3.参数不是具有then方法的对象,或根本就不是对象

如果参数是一个原始值,或者是一个不具有then方法的对象,则Promise.resolve方法返回一个新的 Promise 对象,状态为resolved,并将改参数传入resolved函数处理。

const p = Promise.resolve('111');

p.then(function (s){
  console.log(s)
});
// 111

4.参数为空
Promise.resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。

Promise.resolve().then(function () {
  console.log('111');
});
// 立即输出 111

四、实现 Promise

基础版

const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
    constructor (fn) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined

        this.onFullFilledFn = null
        this.onRejectedFn = null

        let resolve = (value) => {
            if (this.status === PENDING) {
                setTimeout(() => {
                    this.status = FULFILLED
                    this.value = value
                    this.onFullFilledFn(this.value)
                })
            }
        }
        let reject = (reason) => {
            if (this.status === PENDING) {
                setTimeout(() => {
                    this.status = REJECTED
                    this.reason = reason
                    this.onRejectedFn(this.reason)
                })
            }
        }
        try {
            fn(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
}

MyPromise.prototype.then = function (onFullFilled, onRejected) {
    this.onFullFilledFn = onFullFilled
    this.onRejectedFn = onRejected
}


let promise = new MyPromise((resolve, reject) => {
    resolve("同步任务执行")
})
promise.then((val) => {console.log(val)}) // 同步任务执行

这样就是 Promise 的基础版,可以创建promise对象实例,执行成功回调函数和失败回调函数。

在reslove和reject里面用setTimeout进行包裹,是为了使其到then方法执行之后再去执行。要不然会报错 "this.onFullFilledFn is not a function"

三种状态

MyPromise.prototype.then = function (onFullFilled, onRejected) {
    // 如果状态是fulfilled,直接执行成功回调,并将成功值传入
    if (this.status === FULFILLED) {
        onFullFilled(this.value)
    }
    // 如果状态是rejected,直接执行失败回调,并将失败原因传入
    if (this.status === REJECTED) {
        onRejected(this.reason)
    }
    if (this.status === PENDING) {
        this.onFullFilledFn = onFullFilled
        this.onRejectedFn = onRejected
    }
}

then 支持链式操作

现在版本最多只能注册一个回调,我们来实现支持链式回调。

  1. 首先存储回调时要改为使用数组;
  2. 执行回调时,也要改成遍历回调数组执行回调函数;
  3. then 方法返回一个 Promise。
const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
    constructor (fn) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined

        this.onFullFilledFn = []
        this.onRejectedFn = []

        let resolve = (value) => {
            if (this.status === PENDING) {
                setTimeout(() => {
                    this.status = FULFILLED
                    this.value = value
                    this.onFullFilledFn.forEach(fn => fn(this.value))
                })
            }
        }
        let reject = (reason) => {
            if (this.status === PENDING) {
                setTimeout(() => {
                    this.status = REJECTED
                    this.reason = reason
                    this.onRejectedFn.forEach(fn => fn(this.reason))
                })
            }
        }
        try {
            fn(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
    return new MyPromise((resolve, reject) => {
        // 如果状态是fulfilled,直接执行成功回调,并将成功值传入
        if (this.status === FULFILLED) {
            onFullFilled(this.value)
        }
        // 如果状态是rejected,直接执行失败回调,并将失败原因传入
        if (this.status === REJECTED) {
            onRejected(this.reason)
        }
        if (this.status === PENDING) {
            this.onFullFilledFn.push(onFullFilled)
            this.onRejectedFn.push(onRejected)
        }
    })
}

支持串行异步任务

目前then方法里只能传入同步任务,但是我们平常用promise.then方法里一般是异步任务,因为我们用promise主要用来解决一组流程化的异步操作。

const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
    constructor (fn) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined

        this.onFullFilledFn = []
        this.onRejectedFn = []

        let resolve = (value) => {
            if (this.status === PENDING) {
                setTimeout(() => {
                    this.status = FULFILLED
                    this.value = value
                    this.onFullFilledFn.forEach(fn => fn(this.value))
                })
            }
        }
        let reject = (reason) => {
            if (this.status === PENDING) {
                setTimeout(() => {
                    this.status = REJECTED
                    this.reason = reason
                    this.onRejectedFn.forEach(fn => fn(this.reason))
                })
            }
        }
        try {
            fn(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
    const promise = new MyPromise((resolve, reject) => {
        // 如果状态是fulfilled,直接执行成功回调,并将成功值传入
        if (this.status === FULFILLED) {
            setTimeout(() => {
                try {
                    const x = onFullFilled(this.value)
                    resolvePromise(promise, x, resolve, reject)
                } catch(err) {
                    reject(err)
                }
            })
        }
        // 如果状态是rejected,直接执行失败回调,并将失败原因传入
        if (this.status === REJECTED) {
            setTimeout(() => {
                try {
                    const x = onRejected(this.reason)
                    resolvePromise(promise, x, resolve, reject)
                } catch(err) {
                    reject(err)
                }
            })
        }
        if (this.status === PENDING) {
            this.onFullFilledFn.push(() => {
                setTimeout(() => {
                    try {
                        const x = onFullFilled(this.value)
                        resolvePromise(promise, x, resolve, reject)
                    } catch(err) {
                        reject(err)
                    }
                })
            })
            this.onRejectedFn.push(() => {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.value)
                        resolvePromise(promise, x, resolve, reject)
                    } catch(err) {
                        reject(err)
                    }
                })
            })
        }
    })
    return promise
}
function resolvePromise (promise, x, resolve, reject) {
    if (promise === x) {
        return reject(new Error('循环引用'))
    }
    // x 是除了 null 以外的对象或者函数
    let called
    if (x && (typeof x === 'function' || typeof x === 'object')) {
        try {
            const then = x.then
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return
                    called = true
                    resolvePromise(promise, y, resolve, reject)
                }, err => {
                    if (called) return
                    called = true
                    reject(err)
                })
            } else {
                resolve(x)
            }
        } catch (err) {
            if (called) return
            called = true
            reject(err)
        }
    } else {
        resolve(x)
    }
}

实现 catch / all

const PENDING = 'PENDING'
const FULFILLED = 'FULFILLED'
const REJECTED = 'REJECTED'
class MyPromise {
    constructor (fn) {
        this.status = PENDING
        this.value = undefined
        this.reason = undefined

        this.onFullFilledFn = []
        this.onRejectedFn = []
        // 在使用es6 的promise时,可以传入一个异步任务,也可以传入一个同步任务,利用setTimeout特性将具体执行放到then之后
        let resolve = (value) => {
            if (this.status === PENDING) {
                setTimeout(() => {
                    this.status = FULFILLED
                    this.value = value
                    this.onFullFilledFn.forEach(fn => fn())
                })
            }
        }
        let reject = (reason) => {
            if (this.status === PENDING) {
                this.status = REJECTED
                this.reason = reason
                this.onRejectedFn.forEach(fn => fn())
            }
        }
        try {
            fn(resolve, reject)
        } catch (err) {
            reject(err)
        }
    }

    catch (onRejected) {
        // catch 方法就是then方法没有成功的简写
        return this.then(null, onRejected);
    }

    all (promises) {
        //promises是一个promise的数组
        return new MyPromise(function (resolve, reject) {
            let arr = []; //arr是最终返回值的结果
            let i = 0; // 表示成功了多少次
            function processData(index, data) {
                arr[index] = data;
                if (++i === promises.length) {
                    resolve(arr);
                }
            }
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(function (data) {
                    processData(i, data)
                }, reject)
            }
        })
    }
}
MyPromise.prototype.then = function (onFullFilled, onRejected) {
    const promise = new MyPromise((resolve, reject) => {
        // 如果状态是fulfilled,直接执行成功回调,并将成功值传入
        if (this.status === FULFILLED) {
            setTimeout(() => {
                try {
                    const x = onFullFilled(this.value)
                    resolvePromise(promise, x, resolve, reject)
                } catch(err) {
                    reject(err)
                }
            })
        }
        // 如果状态是rejected,直接执行失败回调,并将失败原因传入
        if (this.status === REJECTED) {
            setTimeout(() => {
                try {
                    const x = onRejected(this.reason)
                    resolvePromise(promise, x, resolve, reject)
                } catch(err) {
                    reject(err)
                }
            })
        }
        if (this.status === PENDING) {
            this.onFullFilledFn.push(() => {
                setTimeout(() => {
                    try {
                        const x = onFullFilled(this.value)
                        resolvePromise(promise, x, resolve, reject)
                    } catch(err) {
                        reject(err)
                    }
                })
            })
            this.onRejectedFn.push(() => {
                setTimeout(() => {
                    try {
                        const x = onRejected(this.value)
                        resolvePromise(promise, x, resolve, reject)
                    } catch(err) {
                        reject(err)
                    }
                })
            })
        }
    })
    return promise
}
function resolvePromise (promise, x, resolve, reject) {
    if (promise === x) {
        return reject(new Error('循环引用'))
    }
    // x 是除了 null 以外的对象或者函数
    let called
    if (x && (typeof x === 'function' || typeof x === 'object')) {
        try {
            const then = x.then
            if (typeof then === 'function') {
                then.call(x, y => {
                    if (called) return
                    called = true
                    resolvePromise(promise, y, resolve, reject)
                }, err => {
                    if (called) return
                    called = true
                    reject(err)
                })
            } else {
                resolve(x)
            }
        } catch (err) {
            if (called) return
            called = true
            reject(err)
        }
    } else {
        resolve(x)
    }
}

参考链接:
ES6 - 整理一下Promise 的用法
JavaScript 之 Promise
Promise 原理解析与源码实现
这一次,彻底弄懂 Promise 原理
Promise.resolve()与new Promise(r => r(v))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值