promise

作者: 全凭一口仙气儿活着 | 来源:发表于2019-02-27 12:00 被阅读1次

    前言

    今天来分享下promise的用法,es6伟大发明之一,当初我学习的时候也是蛮头大的,不知道为啥,整个脑子就是,我在哪,我要干啥的懵圈,后面认真学习之后,觉得真是十分好用,下面就来一起学习下吧。

    为什么会有promise

    首先为什么会有promise的存在,其实很多人都知道的,其中最大的问题就是在处理多个有依赖关系的异步操作时,会出现回调地狱( callback hell ),如下:

    $.ajax({
          url: '....',
          success: function (data) {
                $.ajax({
                      url: '....',
                      success: function (data) {
                  }
              });
          }
     });
    
    

    promise提供了一个优雅的方式,来解决这个问题,同时提供了很多的错误捕获机制。

    如何使用promise

    我们先不讲promise的理论语法,这样会一开始就降低学习的欲望,直接来看使用案例,然后去理解。

    首先看基本使用
    new Promise(function (resolve, reject) {
             // 假设此处是异步请求某个数据
                   $.ajax({
                      url: '......',
                       success: function (res) {
                           if (res.code === 200) {
                                resolve(res.data);
                            } else {
                               reject('获取data失败');
                          }
                       }
                   })
    })
    .then(function A(data) {
            // 成功,下一步
          console.log( data);
     }, function B(error) {
            // 失败,做相应处理
           console.log(error)
     });
    console:
    sucess
    error
    

    解析:

    梳理流程:
    • 首先我们在promise函数里,执行我们的异步操作得到data
    • 如果成功的话,通过resolve函数数据传递出来,如果失败。通过reject把错误信息传递出来
    • 然后在.then里可以接受传递出来的数据,.then()里面接受两个函数,第一个函数接收resolve传递出来的值,也就是正确情况下的处理,第二个函数接收reject传递的信息,也就是错误的情况下的处理。
    Promise是一个对象,它的内部其实有三种状态。
    • 初始状态( pending )。
    • 已完成( fulfilled ): Promise 的异步操作已结束成功。
    • 已拒绝( rejected ): Promise 的异步操作未成功结束。

    resolve 方法可以使 Promise 对象的状态改变成成功,同时传递一个参数用于后续成功后的操作。
    reject 方法则是将 Promise 对象的状态改变为失败,同时将错误的信息传递到后续错误处理的操作。

    then(onFulfilled, onRejected)

    ---(onFulfilled, onRejected)

    链式then

    当然,我们既然解决回调地狱,一个异步,看不出来啥优势,现在看多个异步请求, 为了代码简约,我们用setTimeout来代替ajax请求 作为异步操作,如下:

    new Promise((resolve, reject) => {
         setTimeout( () => {
              if (...){
                resolve([1, 2, 3])
            } else {
                reject('error');
            }
       }, 2000);
    })
     .then( data => {
            console.log(value);  // 打印出[1, 2, 3]
            return new Promise( (resolve, reject)=> {   // 新的异步请求,需要promise对象
                let data2 = 1 + data;
                setTimeout( () => {
                  if (...) {
                       resolve(data2);
                  } else {
                      reject('error2')
                  }
                  
                }, 2000);
           });
      }, error => {
          cosnole.log(error)
      })
    .then( data2 => {
          console.log(data2 );
     }, error => {
          cosnole.log(error)
      });
    
    解析:

    -这个例子中,第一个异步操作得到数据[1, 2, 3],传递到第一个then中,我们在第一个then中运用拿到的数据,进行第二次异步操作,并把结果传递出去。在第二个then中拿到数据,并且捕获error。
    可以看到本来嵌套的两个异步操作,现在清晰多了,而且链式接无数个then

    在这里有两个地方需要注意
    • then里面的可捕获错误的函数,可以捕获到上面的所有then的错误,所以只在最后一个then里,写错误捕获函数就可以。
    • 每次异步操作时候需要返回一个新的promise,因为只有用promise对象才会等异步操作执行完,才去执行下面的then,才能拿到异步执行后的数据,所以第二个then里的异步请求,也需要声明Promise对象。如果then里面返回常量,可以直接返回。如下:
    new Promise((resolve, reject) => {
         setTimeout( () => {
              if (...){
                resolve([1, 2, 3])
            } else {
                reject('error');
            }
       }, 2000);
    })
     .then( value => {
            return '222';    // 如果是直接返回常量,可直接return
        })
     .then( value2 => {
         console.log(value2 ); // 打印出222
     })
    

    下面忽略error情况,看两个例子,大家可以自己思考下打印结果

    new Promise(resolve => {
        setTimeout( () => {
            resolve('value1');
        }, 2000);
    })
     .then( value1 => {
            console.log(value1);
            (function () {
                return new Promise(resolve => {
                    setTimeout(() => {
                        console.log('Mr.Laurence');
                        resolve('Merry Xmas');
                    }, 2000);
                });
            }());
            return false;
        })
     .then( value => {
         console.log(value + ' world');
     });
    
    value1
    false world
    Mr.Laurence
    
    new Promise( resolve => {
        console.log('Step 1');
        setTimeout(() => {
            resolve(100);
        }, 1000);
    })
    .then( value => {
         return new Promise(resolve => {
             console.log('Step 1-1');
             setTimeout(() => {
                resolve(110);
             }, 1000);
        })
        .then( value => {
               console.log('Step 1-2');
               return value;
        })
       .then( value => {
             console.log('Step 1-3');
             return value;
        });
    })
    .then(value => {
          console.log(value);
          console.log('Step 2');
    });
    
    console:
    Step 1
    Step 1-1
    Step 1-2
    Step 1-3
    110
    Step 2
    

    catch

    catch 方法是 then(onFulfilled, onRejected) 方法当中 onRejected 函数的一个简单的写法,也就是说可以写成 then(fn).catch(fn),相当于 then(fn).then(null, fn)。使用 catch 的写法比一般的写法更加清晰明确。我们在捕获错误的时候,直接在最后写catch函数即可。

     let promise = new Promise(function(resolve, reject) {
        throw new Error("Explosion!");
    });
    promise.catch(function(error) {
          console.log(error.message); // "Explosion!"
    });
    
    

    上面代码等于与下面的代码

     let promise = new Promise(function(resolve, reject) {
        throw new Error("Explosion!");
    });
    promise.catch(function(error) {
          console.log(error.message); // "Explosion!"
    });
    
    异步代码错误抛出要用reject
    new Promise( resolve => {
        setTimeout( () => {
            throw new Error('bye');
        }, 2000);
    })
    .then( value => {
     })
    .catch( error => {
          console.log( 'catch', error);
     });
    控制台会直接报错 Uncaught Error: bye
    

    解析:因为异步情况下,catch已经执行完了,错误才抛出,所以无法捕获,所以要用reject,如下:

    new Promise( (resolve, reject) => {
        setTimeout( () => {
            reject('bye');
        }, 2000);
    })
    .then( value => {
            console.log( value + ' world');
     })
    .catch( error => {
          console.log( 'catch', error);
     });
    
    catch bye
    利用reject可以抓捕到promise里throw的错
    
    catch 可以捕获then里丢出来的错,且按顺序只抓捕第一个没有被捕获的错误
    new Promise( resolve => {
        setTimeout( () => {
            resolve();
        }, 2000);
    })
    .then( value => {
        throw new Error('bye');
     })
    .then( value => {
       throw new Error('bye2');
     })
    .catch( error => {
      console.log( 'catch', error);
     });
    
    console: Error: bye
    
    
    new Promise( resolve => {
        setTimeout( () => {
            resolve();
        }, 2000);
    })
    .then( value => {
        throw new Error('bye');
     })
    .catch( error => {
      console.log( 'catch', error);
     })
    .then( value => {
       throw new Error('bye2');
     })
    .catch( error => {
      console.log( 'catch', error);
     });
    
    console: Error: bye
    console: Error: bye2
    catch 抓捕到的是第一个没有被捕获的错误
    
    
    错误被捕获后,下面代码可以继续执行
    new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, 1000);
    })
        .then( () => {
            throw new Error('test1 error');
        })
        .catch( err => {
            console.log('I catch:', err);   // 此处捕获了 'test1 error',当错误被捕获后,下面代码可以继续执行
        })
        .then( () => {
            console.log(' here');
        })
        .then( () => {
            console.log('and here');
             throw new Error('test2 error');
        })
        .catch( err => {
            console.log('No, I catch:', err);  // 此处捕获了 'test2 error'
        });
    
    I catch: Error: test2 error
    here
    and here
     I catch: Error: test2 error
    
    错误在捕获之前的代码不会执行
    new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, 1000);
    })
        .then( () => {
            throw new Error('test1 error');
        })
        .catch( err => {
           console.log('I catch:', err);   // 此处捕获了 'test1 error',不影响下面的代码执行
           throw new Error('another error'); // 在catch里面丢出错误,会直接跳到下一个能被捕获的地方。
        })
        .then( () => {
            console.log('and here');
             throw new Error('test2 error');
        })
        .catch( err => {
            console.log('No, I catch:', err);  // 此处捕获了 'test2 error'
        });
    
    I catch: Error: test2 error
    I catch: Error: another error
    
    new Promise(resolve => {
        setTimeout(() => {
            resolve();
        }, 1000);
    })
        .then( () => {
            console.log('start');
            throw new Error('test1 error');
        })
        .then( () => {
            console.log('arrive here');
        })
        .then( () => {
            console.log('... and here');
             throw new Error('test2 error');  
        })
        .catch( err => {
            console.log('No, I catch:', err);   // 捕获到了第一个
        });
    
    No, I catch: Error: test1 error
        at Promise.then (<anonymous>:8:1
    

    Promise.all

    Promise.all([1, 2, 3])
          .then( all => {
              console.log('1:', all);
          })
    [1, 2, 3]
    
    Promise.all([function () {console.log('ooxx');}, 'xxoo', false])
          .then( all => {
             console.log( all);
          });
     [ƒ, "xxoo", false]
    
    let p1 = new Promise( resolve => {
                setTimeout(() => {
                    resolve('I\'m P1');
                }, 1500);
    });
    let p2 = new Promise( (resolve, reject) => {
                setTimeout(() => {
                    resolve('I\'m P2');
                }, 1000);
     });
    let p3 = new Promise( (resolve, reject) => {
                setTimeout(() => {
                    resolve('I\'m P3');
                }, 3000);
     });
    
     Promise.all([p1, p2, p3]).then( all => {
           console.log('all', all);
    }).catch( err => {
            console.log('Catch:', err);
    });
    
    all (3) ["I'm P1", "I'm P2", "I'm P3"]
    
    案例:删除所有数据后,做一些事情、、、、
    db.allDocs({include_docs: true}).then(function (result) {
      return Promise.all(result.rows.map(function (row) {
        return db.remove(row.doc);
      }));
    }).then(function (arrayOfResults) {
      // All docs have really been removed() now!
    });
    
    

    Promise.resolve

    Promise.resolve()
        .then( () => {
            console.log('Step 1');
        })
    

    其他

    Promise.resolve('foo').then(Promise.resolve('bar')).then(function (result) {
      console.log(result);
    });
    VM95:2 foo
    如果你向 then() 传递的并非是一个函数(比如 promise)
    它实际上会将其解释为 then(null),这就会导致前一个 promise 的结果会穿透下面
    

    How do I gain access to resultA here?

    function getExample() {
        return promiseA(…).then(function(resultA) {
            // Some processing
            return promiseB(…);
        }).then(function(resultB) {
            // More processing
            return // How do I gain access to resultA here?
        });
    }
    

    解决 Break the chain

    function getExample() {
        var a = promiseA(…);
        var b = a.then(function(resultA) {
            // some processing
            return promiseB(…);
        });
        return Promise.all([a, b]).then(function([resultA, resultB]) {
            // more processing
            return // something using both resultA and resultB
        });
    }
    

    相关文章

      网友评论

          本文标题:promise

          本文链接:https://www.haomeiwen.com/subject/rprxuqtx.html