Promise 详解

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u013003052/article/details/87894851


ReactNative系列-文章

Promise 详解

前言

Js代码是单线程运行,这就意味着类似于网络请求/数据库查询等耗时操作,需要放在异步进行,返回结果通过回调函数实现。在js回调函数是这样子:

$jquery.get(‘http://www.baidu.com/,  function(data, status) {
    
});

如果有这样的一个需求,在请求登陆接口成功后,再请求获取当前的用户信息,那么网络请求可能会写成这样:

$jquery.post(/login’, function(data, status) {
  if(status === 200) {
    $jquery.get(/memberInfo?id=1, function(data, status) {
      ……
    })
  }
})

上述代码中,我们需要在第一个请求的回调中,判断请求结果,然后再进行第二请求,如果还有需求需要更多的请求,那么上述代码可能会变成这样子:

$jquery.post(.., function(){
  $jquery.post(.., function(){
    $jquery.post(.., function(){
      $jquery.post(.., function(){
        ……
      })
    })
  })
})

很多的callback嵌套,我们简称回调地狱。Promise的出现,可以解决这个问题。使用promise后,回调地狱可以变为这样子:

$jquery.post(‘address1’)
  .then(function(data, status) {
    // 业务处理
    return $jquery.post(‘address2’);
  })
  .then(function(data, status) {
    return $jquery.post(‘address3’);
  })
  .then(function(data, status) {
    return $jquery.post(‘addres4’);
  })
  .catch(function(err) {

  })

定义

Promise是一个代表了异步操作最终完成或者失败的结果对象。它本质上是一个绑定了回调的对象,而不是将回调传进函数内部。常见的promise调用方式,以axios调用举例:

axios.get(‘http://www.baidu.com’)
  .then(function(res) {
    console.log(res);
  })
  .catch(function(err) {
    console.log(err);
  })

上述get方法在axios库的内部,其实已经将promise封装好了,所以看不到promise相关的单词。那么我们如何创建promise函数,可以如同上述代码那样调用呢?

创建和使用

以封装一个ajax的get请求为例:

function get(address) {
  return new Promise(function(resolve, reject){
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
      if(request.readyState === 4) {
        if(request.status === 200) {
          resolve(request.responseText);
        } else {
          reject(request.status);
        }
      }
    };
    request.open('GET', address, true);
    request.send();
  });
}

首先需要在函数体里面创建Promise对象并返回,一般来说是这样的形式: new Promise(function (resolve, reject) {}); 回调函数中有两个参数:resolve, reject。resolve表示程序运行完成,得到正确结果;reject表示程序运行过程中出现错误。然后上述ajax请求promise封装,调用方式:

get('http://www.baidu.com/)
  .then(function(res){
    // 响应的字符串 request.responseText
  })
  .catch(function(e){
    // 错误 request.status
  });

分析:在ajax的异步请求回调里,我们先对请求响应进行判断,如果成功,使用resolve返回结果,它将在.then()函数回调中得到这个结果,如果失败,使用reject返回失败原因,它将在.catch()函数回调中得到原因。

resolve

resolve和reject函数,实际上都是返回promise对象。但是它们内部的状态不一样,分别是:resolve promise和reject promise。而第一个.then能够执行的条件是,“前面”的promise对象中执行了resolve方法。我们可以验证一下:

function get(address) {
  return new Promise(function(resolve, reject){
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
      if(request.readyState === 4) {
        if(request.status === 200) {
          // 此处将resolve方法注释
          // resolve(request.responseText);
        } else {
          reject(request.status);
        }
      }
    };
    request.open('GET', address, true);
    request.send();
  });
}
get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  });

结果:

// 无

结果没有任何输出,程序无法进入then里,我们把上面的函数改一下:

function get(address) {
  return new Promise(function(resolve, reject){
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
      if(request.readyState === 4) {
        if(request.status === 200) {
          resolve(request.responseText);
        } else {
          reject(request.status);
        }
      }
    };
    request.open('GET', address, true);
    request.send();
  });
}
get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  });

结果:

then1  <!DOCTYPE html>。。。。。。
then2  undefined

可以看到第一个then里有回调结果,而第二个then也执行了,但是回调结果是undefined。**通过 .then 形式添加的回调函数,甚至都在异步操作完成之后才被添加的函数,都会被调用,如上所示。**可想而之,.then方法会一直执行下去。如果需要第二个.then拥有回调结果,需要前面的.then返回一个对象或者值。可以验证一下:

get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
    get('http://www.bing.com/'); // 不使用return返回
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  });

结果:

then1  <!DOCTYPE html>。。。。。。
then2  undefined

将resolve promise对象使用return返回:

get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
    return get('http://www.bing.com/'); // 不使用return返回
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  });

结果:

then1  <!DOCTYPE html>。。。。。。
then2  <!DOCTYPE html>。。。。。。

reject

当程序运行错误,需要返回reject promise,当程序reject的时候,会直接被.catch()函数捕获错误,而不会进入到.then()函数。

get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
    return Promise.reject('1');
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  });

结果

then1  <!DOCTYPE html>。。。。。。
reject 1

catch的后续链

当reject进入.catch()函数后,可以继续使用链式操作,类似于Java的try catch后的finally,例如:

get('http://www.baidu.com/')
  .then(function(res){
    console.log('then1', res);
    return Promise.reject('1');
  })
  .then(function(res){
    console.log('then2', res);
  })
  .catch(function(e){
    console.log('reject', e);
  })
  .then(function(){
    console.log('finally');
  });

结果:

then1  <!DOCTYPE html>。。。。。。
reject 1
finally

Promise.all()

Promise.all() 和 Promise.race()是并行运行异步操作的两个组合式工具。

Promise.all()接受Promise对象组成的数组作为参数,它的返回参数也是数组。当promise的全部为resolve后,它才会进入.then()方法,只要有一个promise为reject,它就会进入.catch()。实现如下:

function get(address) {
  return new Promise(function(resolve, reject){
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
      if(request.readyState === 4) {
        if(request.status === 200) {
          resolve('resolve');
        } else {
          reject(request.status);
        }
      }
    };
    request.open('GET', address, true);
    request.send();
  });
}
function r() {
  return new Promise(function(resolve,reject){
    reject('reject');
  })
}
Promise.all([get('http://www.baidu.com/'), get('http://www.bing.com/'), r()])
  .then(function(res){
    console.log(JSON.stringify(res));
  })
  .catch(function(e){
    console.log('catch');
  })

结果:

catch

将reject去掉:

Promise.all([get('http://www.baidu.com/'), get('http://www.bing.com/')])
  .then(function(res){
    console.log(JSON.stringify(res));
  })
  .catch(function(e){
    console.log('catch');
  })

结果:

["resolve", "resolve"]

Promise.race()

Promise.race()接受的参数与Promise.all()一样,不同的是,它会辨别最快达到resolve或者reject的promise对象,如果这个最快是resolve,则进入.then()方法,如果是reject,则进入.catch()方法。

function get(address) {
  return new Promise(function(resolve, reject){
    var request = new XMLHttpRequest();
    request.onreadystatechange = function() {
      if(request.readyState === 4) {
        if(request.status === 200) {
          resolve('resolve');
        } else {
          reject(request.status);
        }
      }
    };
    request.open('GET', address, true);
    request.send();
  });
}
function r() {
  return new Promise(function(resolve,reject){
    setTimeout(function() {
      reject('reject');
    }, 5000);
  })
}
Promise.race([get('http://www.baidu.com/'), get('http://www.bing.com/'), r()])
  .then(function(res){
    console.log(JSON.stringify(res));
  })
  .catch(function(e){
    console.log('catch');
  })

结果:

"resolve"

将reject延时函数改为立即:

function r() {
  return new Promise(function(resolve,reject){
    setTimeout(function() {
      reject('reject');
    }, 0);
  })
}
Promise.race([get('http://www.baidu.com/'), get('http://www.bing.com/'), r()])
  .then(function(res){
    console.log(JSON.stringify(res));
  })
  .catch(function(e){
    console.log('catch');
  })

结果:

catch

与async await配合使用

async函数实际是返回一个promise对象,如果在async函数return某值,实际会被包装为promise的_value值,状态为resolve。

await可以让JavaScript进行等待,它会等待一个promise执行并返回它的结果,JavaScript才会继续往下执行。

如上面的get()请求方法,我们可以这样写:

async function asy() {
  try {
    let respond = await get('http://www.baidu.com/');
  } catch(e) {
    console.log('当get方法中执行reject时')
  }
}

respond的赋值会等待get方法中执行resolve,如果get方法里执行了reject,则会直接被catch函数捕捉。

知识细节

  1. resolve和reject函数,实际上都是返回promise对象,它们只是_state状态不同。
  2. 函数.then能够继续执行的条件是,“前面”的promise对象执行resolve方法。
  3. .then()能够一直顺序执行,直到产生reject,它才会中断,进入.catch()。
  4. .then()方法要获得回调参数,必须要前面有返回值。
  5. .catch()的执行条件是,“前面”的promise对象执行reject方法。
  6. async函数实际是返回一个promise对象,return的值会被包装为promise的_value。
  7. await等待的是一个promise对象。
展开阅读全文

没有更多推荐了,返回首页