Javascript深入系列(九):Promise(待补充完善)

91 阅读4分钟

一、Promises/A 规范

  • Promise 本质是一个状态机
  1. 有三种状态:pending(进行中)、fulfilled(成功)、rejected(失败)
  2. 状态只能由pending变为另外两种的其中一种,且状态转变不可逆:状态转变只能是pending —> fulfilled 或者 pending —> rejected
  3. promise的状态转换只发生一次
  • new Promise时需要传入一个执行器executor,立即执行
  • 支持链式调用,then可以被调用多次
  • then有两个onFullfiledonRejected,可以缺省,取默认值
  • then的返回值:如果是promise,等它执行完,成功走下一个promisethen,失败走下一个promisecatch

二、Promises基本用法

1.1 基本过程

  1. 初始化 Promise 状态(pending
  2. 立即执行 Promise 中传入的 fn 函数,将Promise 内部 resolve、reject 函数作为参数传递给 fn,按事件机制时机处理
  3. 执行 then(..) 注册回调处理数组(then 方法可被同一个 promise 调用多次)
  4. Promise里的关键是要保证,then方法传入的参数 onFulfilledonRejected,必须在then方法被调用的那一轮事件循环之后的新执行栈中执行。
const promise = new Promise((resolve, reject) => {
  let status = true;
  if (status) {
    resolve('操作成功!');
  } else {
    reject('操作失败!');
  }
});

promise.then(res => {
  console.log('成功结果:' + res);
}, error => {
  console.log('失败结果:' + error);
  
});
new Promise((resolve, reject) => {
  resolve();
}).then(res => {
  console.log('success');
}).catch(error => {
  console.log('error');
}).finally(() =>{
  console.log('finally');
})

//success
//finally
let promise = new Promise((resolve, reject)=>{
    reject("拒绝了");
    resolve("又通过了");
});
promise.then((data)=>{
    console.log('success' + data);
}, (error)=>{
    console.log(error)
});

执行结果: "拒绝了"

1.2 事件循环中Promise

Promise代码不同于其他函数,对传入Promise构造方法中的函数不需要显示的调用执行,其会直接执行,且是作为同步任务被执行的。

setTimeout(() => console.log('timeout'))
new Promise((resolve, reject) => {
  console.log('Promise')
});
console.log('main')

/*
 * Promise
 * main
 * timeout
 */
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
}).then(res => {
  console.log(res)  // success
})

二、手写代码

2.1 手写Promise最简20行版本,实现异步链式调用

function Promise(fn) {
  this.cbs = [];

  const resolve = (value) => {
    setTimeout(() => {
      this.data = value;
      this.cbs.forEach((cb) => cb(value));
    });
  }

  fn(resolve.bind(this));
}

Promise.prototype.then = function (onResolved) {
  return new Promise((resolve) => {
    this.cbs.push(() => {
      const res = onResolved(this.data);
      if (res instanceof Promise) {
        res.then(resolve);
      } else {
        resolve(res);
      }
    });
  });
};

2.2 手写简易版本

function myPromise(executor) {
    let self=this;
    self.status='pending';
    self.value=undefined;
    self.reason=undefined;
    function resolve(value) {
        if(self.status==='pending'){
            self.value=value
            self.status="resolved"
        }
    }
    function reject(reason) {
        if(self.status==='pending'){
            self.reason=reason
            self.status=status
        }
    }
    try{
        executor(resolve,reject)
    }
    catch (e) {
        reject(e)
    }
}

2.3 核心案例

new Promise((resolve) => {
  setTimeout(() => {
    resolve(1);
  }, 500);
})
  .then((res) => {
    console.log(res);
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(2);
      }, 500);
    });
  })
  .then(console.log);

三、Async/Await

async/await可以将Promise代码组织的更像同步代码一样,其书写方式就和之前写同步代码一样,只是需要加上相应关键字。
必须在函数上添加async关键字,从而可以在函数内使用await,否则的话会报错。

async function request(id) {
  const result1 = await ajax('/api/user/getInfo', { id })
  // process result1
  const result2 = await ajax('/api/user/getOrder', { id })
  // process result2
  const result3 = await ajax('/api/user/getMessage', { id })
  // process result3
}
request(1)
const asyncFunc = (n) => new Promise(res => setTimeout(() => res(n), 5000))

const call = async (n) => {
  const result = await asyncFunc(n)
  console.log(result)
}
setTimeout(() => {
  console.log('event call!!')  
}, 2000)

call(50)

// event call!!
// 50

四、题目

红灯3秒亮一次,绿灯1秒亮一次,黄灯2秒亮一次,循环执行

var light = function (timmer, cb) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            cb();
            resolve();
        }, timmer);
    });
};

var step = function () {
    Promise.resolve().then(function () {
        return light(3000, red);
    }).then(function () {
        return light(2000, green);
    }).then(function () {
        return light(1000, yellow);
    }).then(function () {
        step();
    });
}

step();
// 输出1,2,4,3
const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve();
  reject()
  console.log(2);
})
promise.then(() => {
  console.log(3);
}, () => {
  console.log("失败的状态")
})
console.log(4);
// 输出 1,2,4,3
new Promise(resolve => {
    console.log(1);
    resolve(3);
  	// 先reslove了,执行then的时候已经是fullfilled,就直接执行了
    Promise.resolve().then(()=> console.log(4)) // 这个promise执行的比外层的早
}).then(num => {
    console.log(num)
});
console.log(2)
// log: 外部promise
// log: 外部第一个then
// log: 内部promise
// log: 内部第一个then
// log: 外部第二个then
// log: 内部第二个then

new Promise((resolve, reject) => {
    console.log("log: 外部promise")
    resolve()
})
    .then(() => {
        console.log("log: 外部第一个then")
        new Promise((resolve, reject) => {
            console.log("log: 内部promise")
            resolve()
        })
            .then(() => {
                console.log("log: 内部第一个then")
            })
            .then(() => {
                console.log("log: 内部第二个then")
            })
    })
    .then(() => {
        console.log("log: 外部第二个then")
    })
// 今日头条面试题
async function async1() {
    console.log('async1 start')
    await async2()
    console.log('async1 end')
}
async function async2() {
    console.log('async2')
}
console.log('script start')
setTimeout(function () {
    console.log('settimeout')
})
async1()
new Promise(function (resolve) {
    console.log('promise1')
    resolve()
}).then(function () {
    console.log('promise2')
})
console.log('script end')

// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// settimeout

参考文章

  1. Promise 技术调研 - 回调地狱的产生原因与解决方式(Jiahao.Zhang's Blog)
  2. ES6 Promise的使用和理解
  3. 【翻译】Promises/A+规范
  4. 图解 Promise 实现原理(一)—— 基础实现

待看:

  1. 这一次,彻底弄懂 Promise 原理
  2. 解读Promise内部实现原理
  3. 手写Promise最简20行版本,实现异步链式调用。(重构版
  4. 手写async await的最简实现(20行)
  5. JS 基础之异步(三):Promise源码实现
  6. 你能手写一个 Promise 吗
  7. 【JS】817- 15 道 Promise 题,你都懂吗?