前端 Promise 它又来了!!!

前端 Promise 它又来了!!!

前言

Promise 是一种异步编程的解决方案,可以认为它是一个容器,里面保存着未来发生的事件结果。 它有三种状态:pending(进行中)fulfilled(已成功)rejected(已失败),状态一旦发生改变就不能再次改变。

什么是回调地狱?

在处理异步请求时,我们一般都会使用回调函数这么处理,这么看完全没毛病,逻辑清晰。

http.post(data,function(res) {
    // do something here
})

但是如果我们需要根据前一个请求返回的结果来发起下一个请求的话,代码则变成了这样:

http.post(data,function(res1) {
  http.post(res1,function(res2) {
    // do something here
  })
})

随着产品和业务逻辑逐渐复杂,可能就会滋生出这种代码 ( 手动狗头 ) :

http.post(data,function(res1){
	http.post(res1,function(res2){
    http.post(res2,function(res3){
      http.post(res3,function(res4){
        http.post(res4,function(res5){
          http.post(res5,function(res6){
              // do something here
          })
        })
      })  
    })  
  })
})

这便是臭名昭著的回调地狱了,带来的负面影响也是不言而喻的:

影响

1,代码臃肿,可读性差
2,耦合程度高,可维护性差
3,只能在回调函数内部处理异常

but… 如果使用 Promise 我们可以写成这样:

fetch(data).then(res1 => {
  return fetch(res1);
}).then(res2 => {
  return fetch(res2);
}).then(res3 => {
  return fetch(res3);
}).catch(err => {
  console.error('err: ', err);
})

Promise 有什么不足吗?

Promise 的链式调用可以让代码变得更加直观,虽然相对看起来逻辑清晰点,但依然还是存在then调用链,有代码冗余的问题,还存在以下的不足:

1,无法取消Promise,一旦新建它就会立即执行,无法中途取消。
2,如果不设置回调函数,promise内部抛出的错误,不会反应到外部。
3,当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成).

这段代码的输出是?为什么?

const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve();
  setTimeout(() => {
    console.log(2);
  })
  reject('error');
})
promise.then(() => {
    console.log(3);
}).then(() => {
    console.log(5)
}).catch(e => console.log(e))
console.log(4);

老生常谈的话题,也就是考察宏任务和微任务。重点主要是:

Promise 函数体内的代码同步执行
先执行宏任务,再执行微任务,执行完微任务后,就再次查找是否有需要执行的宏任务,如此循环往复
Promise 的状态一旦发生变化,就不可以再次改变

正确的输出顺序是1、4、3、5、2

可以手写 Promise 吗?

这里简单的实现一个能够满足then方法链式调用的Promise:

class Promise {
  constructor(params) {
    //初始化state为pending
    this.state = 'pending';
    //成功的值,返回一般都是undefined
    this.value = undefined;
    //失败的原因,返回一般都是undefined
    this.reason = undefined;
    //成功执行函数队列
    this.onResolvedCallbacks = [];
    //失败执行函数队列
    this.onRejectedCallbacks = [];

    //success
    let resolve = value => {
      if (this.state === 'pending') {
        //state change
        this.state = 'fulfilled';
        //储存成功的值
        this.value = value;
        //一旦成功,调用函数队列
        this.onResolvedCallbacks.forEach(fn => fn());
      }
    };

    //error
    let reject = reason => {
      if (this.state === 'pending') {
        //state change
        this.state = 'rejected';
        //储存成功的原因
        this.reason = reason;
        //一旦失败,调用函数队列
        this.onRejectedCallbacks.forEach(fn => fn());
      }
    };
    try {
      params(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }
  then(onFulfilled, onRejected) {
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    onRejected = typeof onRejected === 'function' ? onRejected : err => {
      throw err
    };
    let promise2 = new Promise((resolve, reject) => {
      //当状态是fulfilled时执行onFulfilled函数
      if (this.state === 'fulfilled') {
        //异步实现
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      //当状态是rejected时执行onRejected函数
      if (this.state === 'rejected') {
        //异步实现
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      //当状态是pending时,往onFulfilledCacks、onRejectedCacks里加入函数
      if (this.state === 'pending') {
        this.onResolvedCallbacks.push(() => {
          //异步实现
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.onRejectedCallbacks.push(() => {
          //异步实现
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0)
        });
      };
    });
    return promise2;
  }
  catch(fn) {
    return this.then(null, fn);
  }
}

function resolvePromise(promise2, x, resolve, reject) {
  //循环引用报错
  if (x === promise2) {
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  //防止多次调用
  let called;
  //判断x
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      let then = x.then;
      if (typeof then === 'function') {
        then.call(x, y => {
          if (called) return;
          called = true;
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          if (called) return;
          called = true;
          reject(err);
        })
      } else {
        resolve(x);
      }
    } catch (e) {
      if (called) return;
      called = true;
      reject(e);
    }
  } else {
    resolve(x);
  }
}
//resolve方法
Promise.resolve = function (val) {
  return new Promise((resolve, reject) => {
    resolve(val)
  });
}
//reject方法
Promise.reject = function (val) {
  return new Promise((resolve, reject) => {
    reject(val);
  });
}

const test = new Promise((res, rej) => {
  setTimeout(() => {
    res('resolve after 2000ms');
  }, 2000)
})

test.then(res => {
  console.error('res: ', res);	// res: resolve after 2000ms
})

在这里我们的回调函数用setTimeout实现,把它们放到了宏任务队列里

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
"uncaught (in promise)" 是一个常见的 JavaScript 错误,它通常在使用异步操作时出现。这个错误消息表示 Promise 对象没有被捕获(没有进行错误处理),导致错误未被正确处理。 在 JavaScript 中,Promise 是一种用于处理异步操作的对象。当我们使用 Promise 进行异步操作时,可以通过 .then() 方法来处理 Promise 的返回结果,同时使用 .catch() 方法来捕获错误并进行处理。 如果出现 "uncaught (in promise)" 错误,意味着对 Promise 对象没有进行错误处理。这可能是因为未使用 .catch() 方法来捕获错误,或者 .catch() 方法本身抛出了错误。 为了解决这个错误,我们应该在使用 Promise 对象进行异步操作时,始终使用 .catch() 方法来捕获可能出现的错误,并进行适当的处理。在 .catch() 方法中,我们可以输出错误信息、进行错误提示或任何你认为合适的错误处理方式。 以下是一个示例代码,展示了如何正确处理 Promise 对象中的错误: ``` async function fetchData() { try { const response = await fetch('https://example.com/data'); // 异步操作 const data = await response.json(); // 获取数据 console.log(data); } catch(error) { console.error('出错了:', error); // 错误处理 } } fetchData(); ``` 在这个示例中,我们使用了 async/await 语法,它是一种更简洁地处理异步代码的方式。在 try 代码块中,我们执行了异步操作,同时使用 await 关键字等待 Promise 对象的结果。如果出现错误,就会被 catch 代码块捕获,并进行适当的错误处理。 总之,“uncaught (in promise)” 错误可以通过正确处理 Promise 对象的错误来解决。确保始终使用 .catch() 方法来捕获可能出现的错误,以避免这个错误的发生。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cheng Lucky

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值