javascript 进阶之 - Promise

引言

promise 主要解决了回调地域, 也就是嵌套太深的 callback, 而采用链式方式. 如:

// 普通方式
$.get({
    url:'url',
    success:function(){
        $.get({
            url:'url2',
            success:function(){
                $.get({
                    url:'url3',
                    success:function(){
                        // ...
                    }
                })
            }
        })
    }
})

// promise , 假如 $.get 支持 promise 方式
$.get({
    url:'url1'
})
.then(res=>{
    return $.get({
        url :'url2'
    })
})
.then(res=>{
    console.log('ok')
})
.catch(()=>{
    console.log('error')
})

Promise 对象

  • 构造 Promise 实例时, 参数为一个函数
var p1 = new Promise(function(resolve,reject){
    console.log('promise start')
})

// Promise 内部类似
function Promise(fn){
    var resolve = function(){}
    var reject = function(){}
    fn(resolve,reject);
    return this;
}
  • then, 收集 resolve 后要执行的回调 ; catch , 收集 reject 后要执行的回调
var p1 = new Promise(function(resolve,reject){
    console.log('promise start')
})

p1.then(function(){
    console.log(1)
})
.then(function(){
    console.log(2)
})

// Promise 内部类似
function Promise(fn){
    var resolve = ()=>{
        this.thenList.forEach((fn)=>{
            fn();
        })
    }
    var reject = ()=>{
        this.catchList.forEach((fn)=>{
            fn();
        })
    }

    this.thenList = [] ;
    this.catchList = [];

    fn(resolve,reject);

    this.then = function(callback){
        this.thenList.push(callback);
        return this;
    }

    this.catch = function(callback){
        this.catchList.push(callback) ;
        return this;
    }

    return this;
}

实践发现 , 并不能打印出 1 , 因为 resolve 的执行, 是早于 then 的调用, 这个时候的 thenList 还是个空数组. 所以修改一下, 先让 then 执行. 也就是利用事件循环的原理.

function Promise(fn){
    var resolve = ()=>{
        setTimeout(()=>{
            this.thenList.forEach((fn) => {
              fn();
            })
        },0)
    }; 

    var reject = ()=>{
        setTimeout(()=>{
            this.catchList.forEach((fn) => {
              fn();
            })
        },0)
    }; 

    // 其他不变

}
  • 而还有一个要注意的点就是 , then 的时候, 是可以返回一个新的 Promise 对象, 打断当前链条的. 而返回其他非 Promise 对 then 的链式无影响.

如下, 并未在返回新的 Promise 未打断原 Promise 的 then 链式:

    var p1 = new Promise(function (resolve, reject) {
      console.log('promise start')
      resolve();
    })
      .then(function () {
        console.log(1)
        return new Promise(function (resolve, reject) {
          reject();
        })
      })
      .then(function () {
        console.log(2)
      })
      .catch(function () {
        console.log('error')
      })
// 应该打印 : promise start , 1 , error

可以通过对每次的 then 和 catch 做返回判断, 如果返回的是 Promise 对象, 则停止之前的 then 和 catch 执行, 将剩余未执行的拼在返回的 Promise 的对象的原有 thenList 和 catchList 后面 .
这里顺便也加上参数和状态.

   function Promise(fn) {
      this.status = 'Pending';

      var resolve = (...args) => {
        this.status = 'Resolved';
        setTimeout(() => {
          for (var i = 0; i < this.thenList.length; i++) {
            var result = this.thenList[i](...args);
            if (result instanceof Promise) {
              result.thenList = [...result.thenList, ...this.thenList.slice(i + 1)]
              result.catchList = [...result.catchList, ...this.catchList]
              break;
            }
          }
        })
      }

      var reject = (...args) => {
        this.status = 'Rejected';
        setTimeout(() => {
          for (var i = 0; i < this.catchList.length; i++) {
            var result = this.catchList[i](...args);
            if (result instanceof Promise) {
              result.thenList = [...result.thenList, ...this.thenList]
              result.catchList = [...result.catchList, ...this.catchList.slice(i + 1)]
              break;
            }
          }
        })
      }

      this.thenList = [];
      this.catchList = [];

      fn(resolve, reject);

      this.then = function (callback) {
        this.thenList.push(callback);
        return this;
      }

      this.catch = function (callback) {
        this.catchList.push(callback);
        return this;
      }

      return this;
    }

需要特别说明的是: 这里虽然借用 setTimeout 实现 , 但 promise 和 setTimeout 在事件循环中的表现还是有差异的 , promise 是生成微任务 jobs, 而 setTimeout 则是生成宏任务 , 也就是 task , 每次的 task 是一个任务 , 一个完整的任务包括 jobs .

async / await

  • async 的作用, 隐式创建一个 promise
// 显式 promise
var p1 = new Promise((resolve) => {
   resolve('hello world');
})

// async 隐式创建
var p2 = (async function testAsync() {
   return 'hello async'
})()

// async 无返回时 , 则是类似 resolve(undefined)
var p2 = (async function testAsync() {
})()
  • await , await 表示等待一个 异步的结果, 也就是说 await 相当拿到 then 里面的参数 ;

    1. await 只能在 async 的函数里.
    2. async “修饰” 的函数的返回结果永远返回一个 promise 对象
    3. 通过 await “修饰” 的函数调用, 始终返回其值, 相当于解码, 获取 promise 中 then 的参数; 对于非 async , 非 promise 返回函数, 同样可以用 await , 只是没点用.
async function asyncTest(){
    return Promise.resolve('hello')
}

function sayHi(){
    return 'hi'
}

async function test(){
    var result1 = await asyncTest(); // 'hello'
    var result2 = await sayHi(); // 非 promise 返回也可以 await
    console.log(result1,result2);
    return null ; // 返回什么并不影响其函数结果, 结果始终是一个 promise 对象
}

var p = test();
console.log(p); 
// promise , 并且是 pending 状态, 先于 console.log(result1,result2)执行


// var result = await asyncTest(); 
// 报错 Uncaught SyntaxError: await is only valid in async function


// 待续

总结

以上 Promise 模拟实现, 并非 js 内置实现的 Promise , 只是用简单的代码模拟 , 强调 Promise 其中的几个特性.

  1. Promise 是一个函数.
  2. Promise 的参数是一个函数 ( fn ), 且在生成 Promise 实例时, 这个函数会立即执行.
  3. Promise 的 then 和 catch 的参数也是函数, 在生成 Promise 实例时, 并不会直接执行函数 , 但是会挂载在 Promise 实例上 .
  4. Promise 传入的 fn 执行 resolve 或 reject 时 , 这时候的 Promise 实例必然已经是初始化完成, 即 then 和 catch 都已经挂载完毕.
  5. 第 4 条 , 也就是说明 resolve 和 reject 触发 的 then 和 catch 在同步代码执行之后 . 而且实际上是在 setTimeout 之前.
  6. 在 then 和 catch 中可以返回一个新的 Promise , 打断之前 Promise 链.

相关参考

猜你喜欢

转载自blog.csdn.net/haokur/article/details/80558411