聊聊promise系列(基础)

前言

本文将从浅到深的去剖析promise。因为内容较多,分为上下两篇。javascript

内容大纲

  • 异步编程的解决方案(完成)
  • promise是什么(完成)
  • promise的用法(完成)
  • promise的优势与缺点(完成)
  • promise的错误捕获(待续)
  • promise的原理(待续)
  • promise的底层源码(待续)
  • 本身实现一个promise(待续)

异步编程的解决方案

js是一门单线程的语言,因此其中会涉及到不少异步的操做,异步编程的解决方案有不少种,咱们主要讲一种最基础的和这篇文章的主角(promise):java

回调函数

在异步编程中,这种的应用范围最广,举个定时器的例子:编程

setTimeout(() => {
    // ...
}, 1000);
复制代码

固然在业务中异步请求也会用到不少,但当多个异步操做须要串行操做的时候,就会有回调地狱产生。promise

$.get(url, data1 => {
    console.log(data1)
    $.get(data1.url, data2 => {
        console.log(data2)
        $.get(data2.url, data3 => {
            console.log(data3)
        })
    })
})
复制代码

代码没有美感,且不利于维护。固然,咱们能够经过减小代码嵌套,模块化等手段来修复。可是并不以下面的解决方案优雅。 bash

佼佼者——promise

const reqMethod = (url) => {
    return new Promise((reslove, reject) => {
        $.get(url, data => {
            if(data.success) {
                reslove();
            } else {
                reject();
            }
        })
    })
}

reqMethod(url).then((data1) => {
    return reqMethod(data1.url);
}).then((data2) => {
    return reqMethod(data2.url);
}).then((data3) => {
    return reqMethod(data3.url);
})
复制代码

这样的实现方式,符合易于阅读,由于每一步操做都是按照前后顺序进行的。异步

promise是什么

Promise从不一样角度理解,有不少种含义。模块化

promise的一种承诺

promise从字面意思理解,就是许诺和承诺的意思,对于一种承诺而言,有三种状态:异步编程

一、承诺还未达成,还在纠结过程当中(pending状态)
二、承诺没有实现,失言了(rejected状态)
三、承诺实现了,就是成功的状态(fulfilled状态)
复制代码

Promise是一种标准的规范

在这里不过多展开,你们能够去看Promise/A+ 规范或者ECMAscript规范;函数

Promise是ES6提供的一种对象

Promise是一个对象,是一个构造函数,ES6将其写进了语言标准。统一了用法。最基础的用法以下:ui

const promiseMethod = new Promise((resolve, reject) => {
    // some code
    if(/*异步成功的条件*/) {
        resolve();
    } else {
        reject();
    }
})
复制代码

Promise的用法

new Promise

Promise是一个构造函数,最基础的做用就是用new操做符生成一个实例对象

const promiseMethod = new Promise((resolve, reject) => {
    // some code
    if(/*异步成功的条件*/) {
        resolve();
    } else {
        reject();
    }
})
复制代码

Promise可接受的参数是一个函数,resolvereject是该函数的两个参数,由js引擎提供,不须要本身定义。

resolve的做用是将pending状态变动为fulfilled状态。

reject的做用是将pending状态变动为rejected状态。

Promise新建时就会当即执行,与什么时候调用无关,与结果也无关

举个例子

const promiseOne = new Promise((resolve, reject) => {
    console.log('has finish');
    resolve();
})
复制代码

一、console.log在新建过程当中就执行了。

二、promiseOne的结果已经固定下来了,不管什么时候调用,结果都不会发生改变。

Promise.prototype.then

then方法是被定义在Promise的原型上,做用是:添加Promise状态改变后的回调函数。

then方法接收两个参数,第一个参数是resolve状态执行的函数,第二个参数是reject执行的函数。

then方法返回的是一个新的Promise对象。

举个例子:

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

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

const promiseThree = promiseOne.then(() => {
    console.log('成功的回调')
}, () => {
    console.log('失败的回调')
})

promiseTwo.then(() => {
    console.log('成功的回调')
}, () => {
    console.log('失败的回调')
})

console.log(promiseThree);

复制代码

输出的结果:

has finish
has reject
<Promise>(pending)
成功的回调
失败的回调
复制代码

Promise.prototype.catch

catch方法其实和then第二个回调函数的别名,做用是用于储物发生时的回调处理。

catch捕获两类错误:

一、异步操做时抛出错误,致使状态变为reject

二、then回调过程当中产生的错误。

举个例子:

//第一种状况:异步操做过程当中报错
const promise = new Promise(function(resolve, reject) {
  throw new Error('test');
});
promise.catch(function(error) {
  console.log(error);
});

// 第二种状况:then执行过程当中报错

const promise = new Promise((resolve, reject) => {
    resolve();
})

promise.then(() => {
    throw new Error('test');
}).catch(function(error) {
  console.log(error);
});
复制代码

关于catchthen第二个参数的区别:

// bad
promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

// good
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) {
    // error
  });
复制代码

第二种写法的优势在于,then执行过程当中的报错,catch同样能捕获,优于第一种写法。

更深刻的错误捕获咱们单独放一章讲。

Promise.prototype.finally

finally方法做用在于无论promise返回的状态是什么,它都会在执行。

finally不接受任何参数。

promise
  .then(function(data) {
    // success
  })
  .catch(function(err) {
    // error
  }).finally(() => {
      
  });
复制代码

finally中执行的状态与promise的结果无关,并且在方法中没法得知promise的运行结果。

Promise.all

用法:

Promise.all([p1, p2, p3]);
复制代码

做用:是将多个promise示例封装成一个promise实例。结果只有如下两种情形:

  • 所有成功

全部promise都成功,总的promise才会成功

const p1 = new Promise((resolve, reject) => {
    resolve('hello');
})
const p2 = new Promise((resolve, reject) => {
    resolve('hello');
})
Promise.all([p1, p2]).then(() => {
    console.log('所有成功')
}).catch(() => {
    console.log('所有失败')
})

// 所有成功
复制代码
  • 所有失败

只要有一个promise的状态从pending变成reject就是失败。

const p1 = new Promise((resolve, reject) => {
    resolve();
})
const p2 = new Promise((resolve, reject) => {
    reject();
})
Promise.all([p1, p2]).then(() => {
    console.log('所有成功')
}).catch(() => {
    console.log('所有失败')
})
复制代码

Promise的时间如何计算?

const newDate = new Date();
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve();
    }, 500)
})
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve();
    }, 1000)
})
Promise.all([p1, p2]).then(() => {
    const now = new Date();
    let time = now.getTime() - newDate.getTime();
    console.log(time);
})
// 1001
复制代码

按最长的那个为准。

Promise.race

用法:

Promise.race([p1, p2, p3]);
复制代码

做用:是将多个promise示例封装成一个promise实例。

Promise.race与Promise.all的区别

Promise.race的状态取决于最早完成的promise的状态。

举个例子,咱们须要对一个请求作5秒的timeout,就能够用Promise.race

const reqMethod = (url) => {
    return new Promise((reslove, reject) => {
        $.get(url, data => {
            reslove();
        })
    })
}

const timeout = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject('timeout')
    }, 5000)
})
Promise.race([reqMethod(xxx),timeout]).then(() => {
    console.log('请求成功')
}).catch(() => {
    console.log('timeout')
})
复制代码

Promise.resolve

做用:须要将现有的对象转化为promise对象。

Promise.resolve({});
//等价于
new Promise(resolve => resolve({}));
复制代码

Promise.resolve会根据传入的不一样参数作不一样的处理

  • 传入一个promise对象

不作任何操做,原封不动的返回该对象。

  • 参数是一个thenable对象

当即执行thenable对象中的then方法,而后返回一个resolved状态的promise

let thenable = {
    then: function(resolve, reject) {
        resolve('success')
    }
}

const p = Promise.resolve(thenable);
p.then((e) => {
    console.log(e);
})
复制代码
  • 不是对象

直接返回一个resolved状态的promise。并将参数带给回调函数。

const p = Promise.resolve('参数');

p.then((e) => {
    console.log(e)
})
复制代码
  • 不传参数

直接返回一个resolved状态的promise

Promise.reject

其做用是返回一个新的promise实例,状态直接为reject

Promise.reject('error');
//等价于
new Promise((resolve, reject) => reject('error'));
复制代码

不一样于Promise.resolve,其参数会原封不动的做为reject的理由。

举个例子:

const p = Promise.reject('error');
p.catch((e) => {
    console.log(e)
})
// error
复制代码

Promise的优缺点

Promise的特色

  • 状态不受外界影响,只知足于结果
一、promise表明一个异步操做,一共有三种状态:pending、fulfilled和rejected。
二、promise的结果只服从于异步操做的结果,成功进入fulfilled状态,失败进入rejected状态。
复制代码
  • 状态变动以后不会在改变
一、promise状态变化只有两种可能,一种从pending到fulfilled,或者是从pending到reject。
二、当状态变动完时,状态将不在发生改变。
复制代码

Promise的优势

  • 链式写法可读性比回调函数的写法更优雅。
  • 与回调函数相比更加方便错误处理。
  • 与事件体系相比,一次性返回结果,更加适用于一次性返回的结果。

Promise的缺点

  • 不能取消执行过程。
  • 不能读取异步过程当中的进度。
  • 生成以后对象结果不会改变。

结语

本文对promise的用法进行了详解,以后会更新两篇深刻一点的文章:

  • promise错误捕捉
  • 聊聊promise系列(深刻)
相关文章
相关标签/搜索