Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

异步-Promise #12

Open
shangguanhonglei opened this issue Aug 3, 2018 · 0 comments
Open

异步-Promise #12

shangguanhonglei opened this issue Aug 3, 2018 · 0 comments
Labels
js New feature or request

Comments

@shangguanhonglei
Copy link
Owner

shangguanhonglei commented Aug 3, 2018

同步和异步的区别

同步最大的一个特点是会阻塞,异步不会 ,同步会按照顺序进行执行。

  1. 同步
console.log(1)
alert(2) //阻塞
console.log(3)
//输出顺序为1=>2=>3
  1. 异步
console.log(1)
setTimeout(()=>{
    console.log(2)
})
console.log(3)
//输出顺序为1=>3=>2

事件循环

js本身并没有事件调度机制,“事件调度”都是由宿主环境进行。比如常用的浏览器和Node等。
事件调度的机制可以用几个例子来说明:

  • 例如ajax请求机制

当js发出请求之后就会告诉浏览器“哥们,等到响应数据返回后你就执行这个函数(通常会有一个回调函数)”,然后js引擎就干其他事了。
当服务端返回响应数据后,浏览器会将回调函数加到事件循环中等待执行。

  • 例如click事件

同上可得,当click事件执行时,浏览器会将回调函数放在“事件循环”中,等待执行。

  • 例如setTimeout

一样的道理,js只是设置一个定时器,当定时器到时后,宿主环境会将回调函数放在事件循环中等待执行。

  • 说了这么多,那么到底什么是“事件循环”下面看一段伪代码:
var eventLoop = [];
var event;
//永久执行
while(true){
  if(!!eventLoop.length){
    event = eventLoop.shift();//不断获取下一个
    try{
      event();
    }
    catch(err){
      reportError(err);
    }
  }
}

上面的代码只是事件循环原理的伪代码,真实的代码肯定复杂多了。我们可以很清楚的看到宿主环境只需要将回调函数放在eventLoop队列中,事件循环就会自动获取并执行。

回调地狱

下面的代码是我们经常看到的,常常被称为回调地狱

listen( "click", function handler(evt){
 setTimeout( function request(){
    ajax( "http://some.url.1", function response(text){
      if (text == "hello") {
        handler();
      }
      else if (text == "world") {
        request();
      }
  } );
 }, 500) ;
}); 

Promise.all()执行promise队列

Promise.all([ .. ])接受一个promise数组并返回一个新的promise,这个新的promise等待数组中所有promise完成
下面来看一个代码示例,首先通过异步的方式获取num1和num2。然后将这两个异步对象实例加到Promise.all的执行队列中等待两个异步对象实例都执行完成后获取到values[0]和values[1]也就是num1和num2。进行相加就得到求和之后的结果值

let num1 = 2, num2 = 3;
function add(getX, getY) {
  return Promise.all([getX, getY]).then(function (values) {
    return values[0] + values[1]
  })
}
function getX() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num1)
    }, 1000)
  })

}
function getY() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num2)
    }, 5000)
  })
}
add(getX,getY)

Promise.allSettled() ES2020新增特性

Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。
当您有多个彼此不依赖的异步任务成功完成时,或者您总是想知道每个promise的结果时,通常使用它。
想比较之下, Promise.all() 更适合做相互依赖的Promise,只要有一个失败就结束。

const promise1 = Promise.resolve(100);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, 'info'))
const promise3 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'name'))
Promise.allSettled([promise1, promise2, promise3]).
    then((results) => console.log(result));

/* 
    [
        { status: 'fulfilled', value: 100 },
        { status: 'rejected', reason: 'info' },
        { status: 'fulfilled', value: 'name' }
    ]
*/
//如果status是 fulfilled,那么改对象的另一个属性是 value ,对应的是该Promise成功后的结果。

//如果status是 rejected,那么对象的另一个属性是 reason,对应的是该Promise失败的原因。

在其他地方监听Promise完成

这是一种类似事件监听的机制,promise异步请求完成之后,可以在其他地方监听到,然后对其请求结果进行处理
下面看一个例子

let num1 = 2
let foo = getY() // 获取promise对象实例
bar(foo) //在bar函数监听promise异步完成
baz(foo) //在baz函数监听promise异步完成

//直接监听promise异步完成
foo.then((sucess)=>{
    console.log('成功:sucess:'+sucess)
  },(error)=>{
    console.log('失败:error:'+error)
})
function getY() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(num1)
    }, 5000)
  })
}
function bar (foo){
  foo.then((sucess)=>{
    console.log('bar成功接收到sucess:'+sucess)
  },(error)=>{
    console.log('bar接收失败返回error:'+error)
  })
}
function baz(foo){
  foo.then((sucess)=>{
    console.log('baz成功接收到sucess:'+sucess)
  },(error)=>{
    console.log('baz接收失败返回error:'+error)
  })
}

Promise类型检测

具有then方法的鸭子类型检测

根据一个值的形态(具有哪些属性)对这个值的类型做出一些假定。这种类型检查一般术语用“鸭子类型”来表示。

if(p !==null && (typeof p === 'object'||typeof p === 'function') && typeof p.then === 'function'){
  //假定这是一个Promise类型
}
else{
  //不是
}

这种方式显然不够准确,存在以下问题

  • 对于一个普通的对象比如:var o = { then: function(){} };
    用上面的方法进行检测就会被识别为Promise类型,但事实上他只是一个普通对象而已。

Promise.race() 一种称为竞态的高级抽象机制

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。
换句话说就是如果你有这方面的需求它就可以满足你。如果在协调多个并发promise运行,而只想要相应“第一个跨过终点线的promise”而抛弃其他promise,他就可以满足,这种模式在Promise中成为竞态

链式流

每次对Promise调用then(),他都会创建并返回一个新的Promise,我们可以将其连接起来

let p = Promise.resolve(21) // Promise.resolve() 返回一个给定值解析后的Promise对象
let p2 = p.then( v => {
  return v * 2
})
p2.then(v => {
  console.log(v) //输出42
})

上面的例子是比较简单的链式流,考虑一下下面两种情况

  • 在链式调用中,then的回调函数中还存在异步promise,应该怎么传递?
  • 如果在某一个then函数中由reject进行返回,链式还能否传递下去?

下面分别举例进行说明

  • 先来看第一个问题
let p = Promise.resolve(21)
let p2 = p.then( v => {
  return new Promise((resolve,reject) => {
    resolve(v * 2)
  })
})
p2.then(v => {
  console.log(v) //输出42
})

最终结果依然输出42,这是因为p2已经又常规的then返回的promise实例更新为then回调中 new Promise()返回的promise实例了,这是promise中存在的机制。

  • 下面来看第二个问题
let p = Promise.resolve(21) // Promise.resolve() 返回一个给定值解析后的Promise对象
let p2 = p.then( v => {
  foo.bar() //不存在这一句,故意让出错
  return v * 2
})
p2.then(() => {
  
},(err)=>{
  console.log(err) //输出 ReferenceError: foo is not defined
})

可以看到promise链式流的强大,即使某一个环节出错,链式流还是可以继续传递下去。

@shangguanhonglei shangguanhonglei added js New feature or request 笔记 labels Aug 3, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
js New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant