Promise

78 阅读11分钟

Promise

promise是es6提供异步编程的一种解决方案,比回调函数和事件更合理.

promise是代表了未来将要发生的事件,用来传递异步操作的信息。语法上说就是promise是一个对象,从它可以获取异步操作的消息。

特点:

promise有三种状态:pending(未待定状态)、fulfilled(成功状态)、rejected(失败状态)。

这三种状态,只有pending-->fulfilled 或者pending-->rejected 这两种改变,且一经改变就不会再变,这就是promise(承诺)的由来。

如果改变已经发生,再对promise对象添加回调函数,会立即取得结果。这就可以链式调用。

所以有了promise对象,就可以将异步操作以同步操作流程表达出来。避免了层层嵌套。且promise提供了统一的api方便调用。

缺点:

1.无法取消,一旦建立就立即执行。

2.如果不设置回调函数,promise内部跑出错误不会反映到外部。

3.处于pending状态时,不知道进展到哪一状态,是刚刚开始还是即将完成.

用法

Promise 对象是由关键字 new 及其构造函数来创建。这个构造函数有一个参数叫“处理器函数”,称之为执行器,这个执行器是立即执行的,该执行器有两个参数,都是函数。一个是resove函数和reject函数。当异步任务完成且返回结果值的时候,调用resove函数,当异步任务失败且返回失败原因的时候,调用reject函数。

const promise = new Promise(function(resolve, reject) {  // ... some code  if (/* 异步操作成功 */){    resolve(value);  } else {    reject(error);  }});

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

promise.then(function(value) {  // success}, function(error) {  // failure});

Promise 新建后就会立即执行

let promise = new Promise(function(resolve, reject) {  console.log('Promise');  resolve();});promise.then(function() {  console.log('resolved.');});console.log('Hi!');// Promise// Hi!// resolved



const p1 = new Promise(function (resolve, reject) {  // ...});const p2 = new Promise(function (resolve, reject) {  // ...  resolve(p1);})

上面代码中,p1 和 p2 都是 Promise 的实例,但是 p2 的 resolve 方法将 p1 作为参数,这时 p1 的状态就会传递给 p2。如果调用的时候,p1 的状态是 pending,那么 p2 的回调函数就会等待 p1 的状态改变;如果 p1 的状态已经是 fulfilled 或者 rejected,那么 p2 的回调函数将会立刻执行

then方法

Promise 实例具有then方法,也就是说,then方法是定义在原型对象Promise.prototype上的。

then方法的第一个参数是resolved状态的回调函数,第二个参数是rejected状态的回调函数,它们都是可选的。

then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。

getJSON("/posts.json").then(function(json) {  return json.post;}).then(function(post) {  // ...});

采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用

getJSON("/post/1.json").then(function(post) {  return getJSON(post.commentURL);}).then(function (comments) {  console.log("resolved: ", comments);}, function (err){  console.log("rejected: ", err);});

上面代码中,第一个then方法指定的回调函数,返回的是另一个Promise对象。这时,第二个then方法指定的回调函数,就会等待这个新的Promise对象状态发生变化。如果变为resolved,就调用第一个回调函数,如果状态变为rejected,就调用第二个回调函数

基本上,每一个 Promise 都代表了链中另一个异步过程的完成。

catch方法

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数

getJSON('/posts.json').then(function(posts) {  // ...}).catch(function(error) {  // 处理 getJSON 和 前一个回调函数运行时发生的错误  console.log('发生错误!', error);});

Promise 对象的错误具有"冒泡"性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个 catch 语句捕获。

getJSON('/post/1.json').then(function(post) {  return getJSON(post.commentURL);}).then(function(comments) {  // some code}).catch(function(error) {  // 处理前面三个Promise产生的错误});

一般来说,不要在then()方法里面定义 Reject 状态的回调函数(即then的第二个参数),总是使用catch方法

一般总是建议,Promise 对象后面要跟catch()方法,这样可以处理 Promise 内部发生的错误。catch()方法返回的还是一个 Promise 对象,因此后面还可以接着调用then()方法。

finally()

finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的

server.listen(port)  .then(function () {    // ...  })  .finally(server.stop);

finally方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是fulfilled还是rejected。这表明,finally方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。

finally本质上是then方法的特例。

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

Promise.all 方法接受一个数组作为参数,p1、p2、p3 都是 Promise 对象的实例。(Promise.all 方法的参数不一定是数组,但是必须具有 iterator 接口,且返回的每个成员都是 Promise 实例。)

表示:等所有的状态改变,只有所有的状态是成功,才返回成功,否则就返回失败。

Promise.race方法 和all类似

表示:第一个状态改变了 ,p的状态就改变。这个 Promise 实例的返回值,就传递给p的回调函数。

Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理。

Promise.resolve 方法,Promise.reject 方法
var jsPromise = Promise.resolve($.ajax('/whatever.json'));

上面代码将 jQuery 生成 deferred 对象,转为一个新的 ES6 的 Promise 对象。

如果 Promise.resolve 方法的参数,不是具有 then 方法的对象(又称 thenable 对象),则返回一个新的 Promise 对象,且它的状态为fulfilled。

Promise.resolve('foo')// 等价于new Promise(resolve => resolve('foo'))



const p = Promise.reject('出错了');
// 等同于const p = new Promise((resolve, reject) => reject('出错了'))p.then(null, function (s) {  console.log(s)});// 出错了

ps

ES2020 引入了Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)。所以,它的名字叫做”Settled“,包含了”fulfilled“和”rejected“两种情况。

Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。

const promises = [  fetch('/api-1'),  fetch('/api-2'),  fetch('/api-3'),];await Promise.allSettled(promises);removeLoadingIndicator();

上面示例中,数组promises包含了三个请求,只有等到这三个请求都结束了(不管请求成功还是失败),removeLoadingIndicator()才会执行。

Promise.any()

只要参数实例有一个变成fulfilled状态,包装实例就会变成fulfilled状态;如果所有参数实例都变成rejected状态,包装实例就会变成rejected状态。

Promise.any()Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束。

const promises = [  fetch('/endpoint-a').then(() => 'a'),  fetch('/endpoint-b').then(() => 'b'),  fetch('/endpoint-c').then(() => 'c'),];try {  const first = await Promise.any(promises);  console.log(first);} catch (error) {  console.log(error);}

手写Promise

整理逻辑:

·1.promise是一个类,参数是一个立即执行的执行器,执行器有两个函数参数,resove函数和reject函数,该执行器有三个状态,状态改变:pending-->fufilled状态,pending-->reject 状态,且改变完成后不可更改.

then 方法有两个参数 成功回调函数 和失败回调函数, 在then里判断状态,成功就调成功回调函数,失败就调失败回调函数。

//1.完成promise的简单逻辑
// 1.两个函数 被定义成箭头函数 是要this指向 MyPromise
//1.三个状态属性 定义成const 常量 为了复用
//创建一个promise类
const PENDING = 'pending'
//等待 1.设置常量
const FUFILLED = 'fufilled'
 //成功 1.设置常量
const REJECTED = 'rejected'
 //失败 1.设置常量
class MyPromise {
    //1.constructor接受执行器,执行器立即执行 执行器 两个参数,这两个参数都是函数
    constructor(executor){
        //1.executor(this.resove,this.rejiect)
        //内部错误处理
        try{
            executor(this.resove,this.rejiect)
        }catch{
            this.reject(error)
        }
    }    
//1.promise里的状态
    status = PENDING //默认值
    //1.resove函数的参数
    value = undefined //成功回调传递的值
    //1.reject函数的参数
    reason = undefined //失败回调传递的原因
    //1.resove函数
    resove=(value)=>{
        //1.resove 函数逻辑  状态不是等待 向下执行
        if(this.status !== PENDING) return;
        this.status === FUFILLED ;
        this.value = value ;
    }
    reject=(reason)=>{
         //1.reject 函数逻辑  状态不是等待 向下执行
        if(this.status !== PENDING) return;
        this.status === REJECTED ;
        this.reason = reason ;
    }
    then(successCallBack,failCallback){
        //判断状态 成功就调成功回调函数 失败就调失败回调函数
        if(this.status === FUFILLED){
            successCallBack(this.value)
        }else if (this.status === REJECTED){
            failCallback(this.reason)
        }
    }
    }

2.完成promise 的then方法的异步状态

then方法:需要注意事项

    // 1 then方法里可以同步也可以异步
    // 2.then方法的参数是可选的
    // 3.then方法可以多次调用 当异步情况 多次调用时 我们应该将每一次的回调函数都存储起来
    // 4 then方法链式调用 then参数 回调函数的参数value 是上面一个的返回值
    // 5 then方法返回一个promise对象
    // 6 上一个then方法的返回值给下一个 then方法回调的值
    // 7 then方法不能返回本身这个promise对象 会发生循环调用

//1.完成promise的逻辑
//2.完成promise 的then方法异步状态
const PENDING = 'pending'
 const FUFILLED = 'fufilled'
 const REJECTED = 'rejected'
 class MyPromise {
    constructor(executor){
        try{
            executor(this.resove,this.rejiect)
        }catch{
            this.reject(error)
        }
    }
    status = PENDING
     value = undefined
     reason = undefined
     //成功回调函数初始化
    successCallBack = undefined
    //失败回调函数初始化
    failCallback = undefined
    resove=(value)=>{
        if(this.status !== PENDING) return;
        this.status === FUFILLED ;
        this.value = value ;
        //判断成功回调是否存在,存在就调用
        this.successCallBack &&  this.successCallBack(this.value)
    }
    reject=(reason)=>{
        if(this.status !== PENDING) return;
        this.status === REJECTED ;
        this.reason = reason ;
        //判断成功回调是否存在,存在就调用
        this.failCallback &&  this.failCallback(this.reason)
    }
    then(successCallBack,failCallback){
        //判断状态 成功就调成功回调函数 失败就调失败回调函数
        if(this.status === FUFILLED){
            successCallBack(this.value)
        }else if (this.status === REJECTED){
            failCallback(this.reason)
        }else{ //异步情况 判断等待状态 
           //将成功回调和失败回到存储起来 
           this.successCallBack=successCallBack;
            this.failCallback=failCallback;
        }
    }
  }

//1.完成promise的逻辑
//2.完成promise 的then方法异步状态

//3.完成promise 的then方法多次调用
const PENDING = 'pending'
 const FUFILLED = 'fufilled'
 const REJECTED = 'rejected'
 class MyPromise {
    constructor(executor){
        try{
            executor(this.resove,this.rejiect)
        }catch{
            this.reject(error)
        }
    }
    status = PENDING
     value = undefined
     reason = undefined
         //成功回调函数初始化 变成数组,可以存储多个函数回调
    successCallBack = []
    //失败回调函数初始化  变成数组,可以存储多个函数回调
    failCallback = [] 
   //变成数组,可以存储多个函数回调
    resove=(value)=>{
        if(this.status !== PENDING) return;
        this.status === FUFILLED ;
        this.value = value ;
       // this.successCallBack &&  this.successCallBack(this.value)
        //3 多个回调函数是 循环数组并调用 shift数组方法 从前往后执行 shift 执行一个删掉一个 this.successCallback.shift() 这个返回回调函数 直接调用
       while (this.successCallback.length)this.successCallback.shift()(this.value)
    }    reject=(reason)=>{
        if(this.status !== PENDING) return;
        this.status === REJECTED ;
        this.reason = reason ;
        //this.failCallback &&  this.failCallback(this.reason)
        //使用while循环数组   多个回调函数是 循环数组并调用
        while (this.failCallback.length) this.failCallback.shift()(this.reason)
    }    then(successCallBack,failCallback){
        if(this.status === FUFILLED){
            successCallBack(this.value)
        }else if (this.status === REJECTED){
            failCallback(this.reason)
        }else{
            //调用数组的push
            this.successCallBack.push(successCallBack);
            this.failCallback.push(failCallback);
        }
    }}

//1.完成promise的逻辑
//2.完成promise 的then方法异步状态
//3.完成promise 的then方法多次调用

//4.then方法的链式调用(返回promise) 以及将上一个then方法的返回值 promise对象传递给下一个
//5 then方法参数的可选
const PENDING = 'pending' 
const FUFILLED = 'fufilled' 
const REJECTED = 'rejected' 
class MyPromise {    
    constructor(executor){
        try{
            executor(this.resove,this.rejiect)
        }catch{
            this.reject(error)
        }    }    
     status = PENDING     
     value = undefined
     reason = undefined
     successCallBack = []
    failCallback = []
// 变成数组,可以存储多个函数回调
    resove=(value)=>{
        if(this.status !== PENDING) return;
        this.status === FUFILLED ;
        this.value = value ;
        while (this.successCallback.length)this.successCallback.shift()()
    }
    reject=(reason)=>{
        if(this.status !== PENDING) return;
        this.status === REJECTED ;
        this.reason = reason ;
        while (this.failCallback.length) this.failCallback.shift()()
    }   
   then(successCallBack,failCallback){
         //  将then方法参数变为可选参数存在就调用 不存在降至传递下去
        successCallback = successCallback ? successCallback : value => value
        failCallback = failCallback ? failCallback : reason => { throw reason }
        //创建一个promise对象
         let promise2 = new MyPromise((resolve,reject)=>{
            if(this.status === FUFILLED){
               setTimeout(()=>{
               //将返回值传递给下一个then调用
               let x = successCallBack(this.value)
               //判断x的值是普通纸还是promise
               //如果是普通纸 直接调动resolve
               // 如果是promise对象 查看promise对象返回的结果
               // 再根据peomise对象返回的结果 决定调用resolve还是调用reject
               resolvePromise(promise2,x,resolve,reject)
               })
            }else if (this.status === REJECTED){
                // failCallback(this.reason)//失败回调 传递原因
                setTimeout(() => {
                    let x = failCallback(this.reason)
                    resolvePromise(promise2, x, resolve, reject)
                }, 0)
            }else{
                //调用数组的push
                //this.successCallBack.push(successCallBack);
               // this.failCallback.push(failCallback);
                   this.successCallback.push(() => {
                                      setTimeout(() => {
                           try{ //捕捉错误
                                let x = successCallback(this.value)
                                resolvePromise(promise2, x, resolve, reject)
                            }catch(e){
                                reject(e)
                            }
                        }, 0)
                })                
            // this.failCallback.push(failCallback)
                this.failCallback.push(() => {
                    setTimeout(() => {
                          try{ //捕捉错误
                              let x = failCallback(this.value)
                              resolvePromise(promise2, x, resolve, reject)
                          }catch(e){
                              reject(e)
                          }
                    }, 0)
                })
            }
                     }
        return promise2;
    }} 
function resolvePromise(promise, x, resolve, reject) {
     if (promise === x) {
         return reject(new TypeError("Chaining cycle detected for promise #<Promise> "))
     }
     if (x instanceof MyPromise) {
        // x.then(value=>resolve(value),reason=>reject(reason)) 与下等同
         x.then(resolve, reject)
     } else {
         resolve(x)
     } }


ps:下篇讲解all方法 resolve方法 finally方法 catch方法的实现