JavaScript的Promise

316 阅读4分钟

JavaScript的Promise

在了解promise之前我们要先了解什么是异步编程
javascript异步编程
我们知道js的核心特征就是单线程,而为了利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程,但是子线程完全受主线程控制,且不得操作 DOM。 本质还是单线程。
那么什么是同步什么是异步? 举个例子:
我们去奶茶店买奶茶,发现好多人都在买店外排了一条长长的队,而奶茶店缺少人手就只有一个店员在工作。我们要等好久才能排到我们。【这就是同步】如果这时有一个客户和店员发生了冲突并且要好久才能解决,那我们也要等更久才能买到奶茶【这就是阻塞】
这时奶茶店老板路过发现排的队太长影响其他店工作,就让我们排成两排,并临时找了个员工工作,我们也就不用等多久就轮到我们了。【这就是异步】如果有一个客户和店员发生冲突,这就不会影响我们的进度。

async-sync.png

了解完异步和同步就开始了解promise
Promise 是异步编程的一种解决方案,其实是一个构造函数, Promise对象有以下两个特点。

  • 对象的状态不受外界影响。promise英文意思是诺言,表示不可改变的。Promise对象有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)只有异步操作的结果。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果 先来创建一个Promise对象
    接收两个参数:resolve(解析)和 reject(拒绝)
let p = new Promise(function(resolve, reject){
//做一些异步操作
    setTimeout(function(){
	console.log('执行完成Promise');
	resolve('要返回的数据可以任何数据例如接口返回数据');
    }, 2000);
});

// [执行过程] 2秒后,输出“执行完成Promise”,并且调用resolve方法。

完整实例

.then() 可以将参数中的函数添加到当前 Promise 的正常执行序列,.catch() 则是设定 Promise 的异常处理序列,.finally() 是在 Promise 执行的最后一定会执行的序列。 .then() 传入的函数会按顺序依次执行,有任何异常都会直接跳到 catch 序列:

new Promise(function (resolve, reject) { 
    console.log(1111); 
    resolve(2222); 
}).then(function (value) { 
    console.log(value); 
    return 3333; 
}).then(function (value) { 
    console.log(value); 
    throw "An error"; 
}).catch(function (err) { 
    console.log(err); 
});

// [执行结果]  1111     2222     3333    An error

Promise例子
生成一个0-2之间的随机数,如果小于1,则等待一段时间后返回成功,否则返回失败:

    new Promise(function (resolve, reject) {
      var timeOut = Math.random() * 2;
      setTimeout(function () {
        if (timeOut < 1) {
          resolve('200 OK');
        }
        else {
          reject('timeout in ' + timeOut + ' seconds.');
        }
      }, timeOut * 1000);
    }).then(function (r) {
      console.log('成功: ' + r);
    }).catch(function (reason) {
      console.log('失败: ' + reason);
    });

// [执行结果] 失败: timeout in 1.292690671169273 seconds.
// [执行结果] 成功: 200 OK

有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。

// 先增加,再查询,后修改,最后删除
var p = new Promise(function (resolve, reject) {
      resolve(123);
    });
    function add(data) {
      return new Promise(function (resolve, reject) {
        console.log('增加成功')
        setTimeout(resolve, 500, data + data);
      });
    }
    function query(data) {
      return new Promise((resolve, reject) => {
        console.log('查询成功')
        setTimeout(resolve, 500, data + data);
      })
    }
    function update(data) {
      return new Promise((resolve, reject) => {
        console.log('修改成功')
        setTimeout(resolve, 500, data + data);
      })
    }
    function deleted(data) {
      return new Promise((resolve, reject) => {
        console.log('删除成功')
        setTimeout(resolve, 500, data + data);
      })
    }

    p.then(add).then(query).then(update).then(deleted).then(function (result) {
      console.log('Got value: ' + result);
    })
    
    // [执行结果] 增加成功 查询成功 修改成功 删除成功 Got value: 1968

让函数返回一个Promise对象

返回一个resolve

const promise = () => {
      return Promise.resolve('1111')
      .then((data) => { console.log('第一个:', data) })
      .then((data) => { console.log('第二个:', data); return '222'; })
      .then((data)=>{console.log('第三个:', data)})
      .catch(err => {console.log("err"); return Promise.reject('eee');})
}
    
    console.log('先运行1')
    promise()
    console.log('后运行2')
// [执行结果] 先运行1 后运行2   第一个: 1111    第二个: undefined    第三个: 222

我们发现后运行2打印在promise之前,这是有关宏任务和微任务的知识点稍后再了解
返回一个reject


function foo() {
    return Promise.resolve('aaa')
            .then(data => { console.log("then1: ", data); })
            .then(data => { console.log("then2: ", data); return 'bbb'; })
            .then(data => { console.log("then3: ", data); return Promise.reject('ccc'); })
            .catch(err => { console.log("error: ", err) });
}

foo()

// [执行结果] then1:  aaa then2:  undefined then3:  bbb   error:  ccc

宏任务和微任务

宏任务:seTimeout setInterval Ajax DoM事件

微任务:promise async/await

规则:

例题:

    console.log("AAAA");

    setTimeout(() => console.log("BBBB"), 1000);

    const start = new Date();

    console.log("CCCC");

    setTimeout(() => console.log("DDDD"), 0);

    new Promise((resolve, reject) => {
      console.log("EEEE");
      foo.bar(100);
    })
    .then(() => console.log("FFFF"))
    .then(() => console.log("GGGG"))
    .catch(() =>console.log("HHHH"));

    console.log("IIII");

打印顺序

// (宏任务)输出`AAAA`
AAAA     
//  setTimeout(宏任务) 由于他需要延迟一秒错过了本次宏任务所以只能等微任务完成后才能再此排到它
// 继续输出 CCCC
CCCC      
// new Promise将执行promise函数,它参数是一个回调函数,这个回调函数内的代码是同步的
EEEE
// 由于Promise.then().catch()是微任务
// 优先执行宏任务
IIII
//第一次宏任务执行完毕,检查是否有微任务,有则执行微任务,无则执行第二次宏任务
// 存在微任务Promise.then().catch() 执行
HHHH
// 微任务执行完毕,执行第二次宏任务 这里setTimeout由于延迟时间DDDD优先BBBB
DDDD
BBBB

// setTimeout是单线程,类似异步,但不是异步 。

Promise的执行顺序
romise是宏任务(同步执行)但Promise 的回调函数属于异步任务,会在同步任务之后执行(比如说 then、 catch 、finally)。
romise 的回调函数不是正常的异步任务,而是微任务(microtask)它们的区别在于,正常任务追加到下一轮事件循环,微任务追加到本轮事件循环。这意味着,微任务的执行时间一定早于正常任务。

hong.png