ES6 Promise 执行解析

作为一门单线程的语言,刚学习 JavaScript 语言的时候,我曾怀疑过 JavaScript 在处理 ajax 数据请求,文件解析等过程效率会很低,而且在执行这些任务较大的代码中,会严重阻塞后面代码的执行。但让人兴奋的是,这门语言具有异步加载的特性。

事件循环

1. 含义

javascript 提供一种机制来处理程序中多个块的执行,且每个执行块都调用了 JavaScript 引擎,这种机制被称为事件循环。

2. setTimeout

setTimeout 在不清楚它的调用机制的时候,可能会容易掉坑。一定要清楚的是,setTimeout 执行的时候,并没有把回调函数放入事件循环队列中,它做的事情只是设置一个定时器,当时间到时后,环境才会把回调函数加入到事件循环队列中

考虑下面的例子

setTimeout(function(){
    console.log("setTimeout")
},0)
console.log("console")
// console,setTimeout
复制代码

根据 setTimeout 的执行机制,这个例子的先后执行顺序就一目了然了。即便 setTimeout 的定时器时间为0,但是因为事件循环机制,且考虑到浏览器实行的延迟作用,setTimeout 依旧还是会慢与外界的 console。

回调

1.含义

回调是编写和处理 JavaScript 程序异步逻辑最常用的方式。

2.嵌套回调和链式回调

考虑:

listen('click',function handle(){
    setTimeout(function request(){
        ajax('http:XXX',function response(data){
            if(data.code===200){code...}
            else{code...}
        })
    },
    500)
})
复制代码

在我初学 javascript 的时候,常写这种函数嵌套在一起构成一条链的代码,因为它能按照我们想要执行的顺序执行,但是这种代码在嵌套多层的时候是很容易让人混乱的,这种代码常常被称为回调地狱

不用嵌套将上述代码重写,可写为:

listen('click',handle)
function handle(){
    setTimeout(request,500)
}
function request(){
    ajax('http:XXX',response)
}
function response(data){
    if(data.code===200){code...}
    else{code...}
}
复制代码

3.总结

通过上述例子我们可以初步的理解回调的意义,下面将会讲解异步最常使用的 Promise。

Promise

1.基础知识

Promise(译为期望,期待),是一种封装和组合未来值的易于复用的机制;

1.1信任问题

  1. 未能传递参数/环境值(如果使用多个参数调用 resolve() 或者 reject() 第一个参数之后的所有参数都会被默认忽略
  2. 吞掉错误或异常:在创建 promise 或查看决议的过程中,出现 typeerror 或者 referenceerror,那么这个异常会被捕获,并且会使这个 promise 被拒绝
  3. 是可信任的 promise :1)向 promise.resolve() 传递非 promise 非 thenable 会得到用这个值填充的 promise 2)向 promise.resolve() 传递一个真正的 promise 只会返回同一个 promise

1.2 链式流

调用 then() 时的完成处理函数或拒绝处理函数,如果抛出异常,都会导致链中下一个 promise 因为这个异常而立即被拒绝。如果没有传入拒绝处理函数,那么会使用默认拒绝处理函数。(默认拒绝处理函数,只是把错误重新抛出,使链接的 promise 用同样的错误理由拒绝,从本质来说,这使得错误可以继续沿着 promise 链传播下去,直到遇到显式定义的拒绝函数。)

注:当给 promise.resolve() 传递的是 promise 或是一个 thenable ,promise.resolve() 会直接返回接收到的 promise,或展开接收到的 thenable 的值。但 promise.reject() 不会像 resolve() 一样进行展开。如果向 reject() 传入 promise/thenable 会将此值原封不动的设置为拒绝理由。

1.3 错误处理

对于只有 reject 的then,可以直接使用 .catch()

1.4 promise 模式

  1. Promise.all([]) 接收一个数组,每个元素都是一个 Promise,主 Promise 在且仅在所有的成员 promise 都完成后才会完成
  2. Promise.race([]) 这种模式成为 门闩,竞态。一旦有一个 promise 决议为完成,Promise.race([]) 就会完成。一旦有任何一个 promise 决议为拒绝,他就会拒绝。(若数组为空,永远不会决议)
  3. Promise.none([]) 与all([]) 相反,当所有的 promise 都是拒绝的时候,即拒绝转换为完成值。
  4. Promise.any([]) 只需要有一个完成,即可转化为完成值
  5. Promise.first([]) 只要第一个 promise 完成就会忽略后续的任何拒绝和完成
  6. Promise.last([]) 只要最后一个 promise 完成就会忽略后续的任何拒绝和完成。

2.语法

下面代码创造了一个 Promise 实例

const myPromise = new Promise(function(resolve,reject){
    // ...code
    if(/异步执行成功/) {
        resolve(value)
    } else {
        //异步执行失败
        reject(error)
    }
复制代码

resolve函数的作用是,当 Promise 的状态由未完成转变为成功时调用的函数,reject函数的作用是,当 Promise 的状态由未来城转变为失败时调用的函数
Promise 实例生成后,可以使用 then 方法分别指定 resolved 状态和 rejected 状态的回调函数

myPromise.then(function(value){
    // success
    },function(error){
        //error
    }
)
复制代码

then的方法可以接收两个参数,第一个回到函数是当 Promise 状态变为成功 resolved 时调用,第二个回调函数是当 Promise 状态变为失败 rejected 时调用,第二个参数是可选的,非必须的

3.实战练习

根据上面的事件循环机制,以及 Promise 的语法,考虑下方代码:

let myPromise = new Promise(function(resolve,reject){
    console.log("promise");
    resolve()
})
myPromise.then(function(){
    console.log("resolved.")
})
console.log("consolelog")
// promise
//consolelog
//resolved.
复制代码

上面代码中,Promise 新建后立即执行,所以首先输出的是 “Promise”,然后,then 方法指定回调函数,将当前脚本所有同步任务执行完之后再调用,所以 ,然后输出 “consolelog”,最后执行 then ,输出“resolved."

进而再考虑下面代码:

setTimeout(function(){console.log("setTimeout")},0)
let myPromise = new Promise(function(resolve,reject){
    console.log("promise");
    resolve()
}).then(function(){
    console.log("resolved.")
})
console.log("consolelog")
// promise -> consolelog -> resolved. -> setTimeout
复制代码

这段代码即是上面代码加上 setTimeout ,原理是相同的,因为 setTimeout 存在延迟,即便延迟时间为0,它都不属于 Promise 所在的同步任务事件队列中,所以,setTimeout 会在最后执行。

思考题

如何实现下方代码连续打印出 0,1,2.。。9

for (var i=0;i<10;i++) {
	setTimeout(()=>{
		console.log(i)
	},1000)
}
// 打印出 10 个 10
复制代码

方案一:setTimeout 传入 i,保持 i

for (var i=0;i<10;i++) {
	setTimeout((i)=>{
		console.log(i)
	},1000,i)
}
复制代码

方案二:将 var 修改为 let,因为 let 会重新定义一个全新的 i

for (let i=0;i<10;i++) {
	setTimeout(()=>{
		console.log(i)
	},1000)
}
复制代码

方案三:使用立即函数调用的方式

for (var i=0;i<10;i++) {
	(function(i){
		setTimeout(()=>{console.log(i)},1000)
	})(i);
}复制代码

总结

本文主要讲解了 javascript 的事件循环机制,代码自上而下执行过程中,是存在异步执行的过程的,但在当前所有同步任务的执行依旧是自上而下的。另外,本文主要讲解了 Promise 的主要用法,详情可以参照 es6.ruanyifeng.com/#docs/promi…


猜欢迎大家在留言区探讨更多的  Promise 使用~

喜欢的点个赞呗~

猜你喜欢

ES6 箭头函数 www.jianshu.com/p/24d488844…

ES6 生成器 generator www.jianshu.com/p/91673eab0…



转载于:https://juejin.im/post/5c7cc1c46fb9a049e12aee7e

你可能感兴趣的:(ES6 Promise 执行解析)