Promise

  1. 背景

    • javascript语言的一大特点就是单线程,在某个特定的时刻只有特定的代码能够被执行,并阻塞其它的代码,也就是说,同一个时间只能做一件事。
    • 怎么做到异步编程?回调函数。直到nodejs的出现,开始将回调模式的异步编程机制发挥的淋漓尽致,这种机制开始在前端变得非常流行,但是慢慢也体现出了回调函数在错误处理嵌套上的副作用。
    • 因为存在上面的不足,所以异步解决方案一直在发展中,从 callback => promise => generator => async/await => rx => .....
  2. 简单了解event loop

    +javascript上, 所有同步任务都在主线程上执行,也可以理解为存在一个“执行栈”。

    • 主线程外,还有一个“任务队列”,任务队列的作用,就在等待异步任务的结果,只要异步任务有了运行结果,就会加入到“任务队列”中。

    • 一旦执行栈中所有同步任务执行完毕,就从 任务队列 中读取“任务”加入到“执行栈”中。

    • 主线程不断的在循环上面的步骤。


      (function() { console.log('这是开始'); setTimeout(function cb() {
      console.log('这是来自第一个回调的消息');
      setTimeout(function cb3() {
      console.log("这是来自第三个回调的消息");
      })
      }); console.log('这是一条消息'); setTimeout(function cb1() {
      console.log('这是来自第二个回调的消息');
      setTimeout(function cb3() {
      console.log("这是来自第四个回调的消息");
      })
      }); console.log('这是结束'); })();
  3. 什么是Promise

    • Promise代指那些尚未完成的一些操作,但是其在未来某个时间会返回某个特定的结果,成功或者失败。
    • 语法上来讲,promise是一个对象,代表一个未知的值,当值返回时,Promise总是处于下面的三种状态之一:
      • pending:等待。
      • resolved: 已完成。
      • rejected: 已拒绝。
    • 状态只可能从pening -> resolved | pending -> rejected,并且一旦改变,就不会再发生变化了。
    • promise对象必须是thenable的,而且then必须返回一个promise。
  4. 为什么要使用Promise

  • 代码看起来更符合逻辑,可读性更强。
  • 解决回调地狱
  • 更好的捕获错误
  1. 举几个栗子
  • 最简单的Promise

这里看起来很简单,但有两点是要注意的

  1. 一定要resolve或者reject,否则你的then是永远也执行不到的。
  2. promise的状态一定改变后,就再也不会发生变化了。
  let promise = new Promise(function(resolve, reject) {
resolve("success");
reject("fail");
});
promise.then((value) => {
console.log(value);
}).catch((reason) => {
console.log(reason);
});

输出结果

这个例子也充分证明了Promise只有一个状态结果,并且是不可变的

  • 经典的回调地狱

    • 回调函数的写法

    多个异步事务多级依赖,回调函数会形成多级的嵌套,代码就会变成金字塔结构,不仅可读性不高,而且在后期的维护,调试或者重构上,都充满了风险。

doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);},
failureCallback);
  • promise的写法

解决了嵌套问题,thenable可以更好的支持链式调用,但还是能看到回调的影子

更加简便的错误处理


doSomething()
    .then(function(result) {
        return doSomethingElse(result);
    })
    .then(function(newResult) {
        return doThirdThing(newResult);
    })
    .then(function(finalResult) {
        console.log('Got the final result: ' + finalResult);
    })
    .catch(failureCallback);

// 配合箭头函数


doSomething()
    .then(result => doSomethingElse(result))
    .then(newResult => doThirdThing(newResult))
    .then(finalResult => {
    console.log(`Got the final result: ${finalResult}`);
    })
    .catch(failureCallback);
  • generator写法

有一个暂停状态,只有我们激活next,才会去执行下一个异步任务

  function* fuc() {
const result = yield doSomething()
const newResult = yield doSomethingElse(result)
const finalResult = yield doThirdThing(newResult)
}
const test = fuc()
const result = test.next() // {value: 1, done: false}
const newResult = test.next(result.value) // {value: 1, done: false}
const finalResult = test.next(newResult.value) // {value: 1, done: true}
test.next() // {value: undefined, done: true}
  • async/await的写法

这个语法上更加简单,看起来更像同步任务,而且不需要关心执行状态。

我理解这里只是对generator的一个包装,里面应该有个递归函数,在执行next,执行done。


async function useAsyncAwait() {
    try {
        const result = await doSomething()
        const newResult = await doSomethingElse()
        const finalResult = await doThirdThing()
        console.log('Got the final result: ' + finalResult)
    } catch (e) {
        Console.error('exception: ', e)
    }
}
  • 如何包装promise
 function getJSON(url) {
return new Promise(function(resolve, reject){
let xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = handler;
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.send();
function handler() {
if (this.readyState === this.DONE) {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
}
}
};
});
} getJSON('/posts.json')
.then((json) =>{
// on fulfillment
})
.catch((error) => {
// on rejection
console.log(error)
});
  • 多个promise顺序执行

promise的then,是可以保证按照你看到的顺序执行的

  getJSON('/ray/api/a/server/ping')
.then((json) =>{
// on fulfillment
console.log('ping a result: ' + JSON.stringify(json));
})
.then(() => getJSON('/ray/api/b/server/ping'))
.then(json => {
console.log('ping b result: ' + JSON.stringify(json))
})
.catch((error) => {
// on rejection
console.log(error)
}); // ping a result: {"status":"OK","serverTime":1573554742633}
// ping b result: {"status":"OK","serverTime":1573554742667}
  • Promise 的链式调用

举一个更具体的例子,体现thenable, promise的状态不可变


var p = new Promise(function(resolve, reject){
resolve(1);
});
p.then(function(value){ //第一个then
console.log(value); // 1
return value*2;
}).then(function(value){ //第二个then
console.log(value); // 2
}).then(function(value){ //第三个then
console.log(value); // underfined
return Promise.resolve('resolve');
}).then(function(value){ //第四个then
console.log(value); // 'resolve'
return Promise.reject('reject');
}).then(function(value){ //第五个then
console.log('resolve: '+ value); // 不到这里,没有值
}, function(err){
console.log('reject: ' + err); // 'reject'
})
  • 引用一些第三方库 比如 Bluebird

比如

mapSeries => 同步的执行所有异步任务,

all => 等待并发的任务全部执行完毕,

any => 多个异步任务中,有一个执行完毕就结束了。

==


npm install bluebird import * as Promise from "bluebird"; let data = ['a', 'c', 'b', 'e', 'd']
Promise.mapSeries(data, (d) => getJSON(d) ).then((result) => {console.log(result)}) // 执行结果应该是什么 1. 如果没有修改代码 => 只执行了第一个a,后面的都应该第一个a出错,所以不继续执行了 2. 如果将getJSON里的reject改成resoleve => a c b e d的出错log会按顺序打印
  • 多个promise并发执行

Promise.all([ getJSON('/ray/api/a/server/ping'), getJSON('/ray/api/b/server/ping')]).then((result) => {
console.log('ping result: ' + JSON.stringify(result));
}) // ping result: [{"status":"OK","serverTime":1573554934072},{"status":"OK","serverTime":1573554934070}]
  • 多个promise之间的竞争

promise 无法取消执行

new Promise 里的任务会立即执行


const delay = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('timeout');
resolve('timeout');
}, 3000)
}) Promise.race([ getJSON('/ray/api/a/server/ping'), delay]).then((result) => {
console.log('ping result: ' + JSON.stringify(result));
}) // 思考下这里的timeout会不会打印
  • Promise 的穿透

promise 的then必须接收函数,否则会穿透。


// 这里`Promise.resolve(2)`并不是函数,所以上一个函数的结果会穿透到下一个
Promise.resolve(1).then(Promise.resolve(2)).then((v) => {
console.log(v)
}) // 语法错误
Promise.resolve(1).then(return Promise.resolve(2)).then((v) => {
console.log(v)
}) // 穿透
Promise.resolve(1).then(null).then((v) => {
console.log(v)
}) // 语法错误
Promise.resolve(1).then(return 2).then((v) => {
console.log(v)
}) // then会返回新的promise,并且带上他返回的结果
Promise.resolve(1).then(() => {
return 2
}).then((v) => {
console.log(v)
})

当then()受非函数的参数时,会解释为then(null),这就导致前一个Promise的结果穿透到下面一个Promise。所以要提醒你自己:永远给then()传递一个函数参数。

  • 错误的捕获

一旦捕获到错误,promise的then会继续执行

catch会检查promis链上位于它之前的每个地方(then或者其他异步操作)。如果在它之前还有其他catch,那么起点就是上一个catch。

var p1 = new Promise( function(resolve,reject){
foo.bar();
resolve( 1 );
}); p1.then(
function(value){
console.log('p1 then value: ' + value);
},
function(err){
console.log('p1 then err: ' + err);
}
).then(
function(value){
console.log('p1 then then value: '+value);
},
function(err){
console.log('p1 then then err: ' + err);
}
);

轻喷!

理解js异步编程的更多相关文章

  1. 深入理解JS异步编程五(脚本异步加载)

    异步脚本加载 阻塞性脚本 JavaScript在浏览器中被解析和执行时具有阻塞的特性,也就是说,当JavaScript代码执行时,页面的解析.渲染以及其他资源的下载都要停下来等待脚本执行完毕 浏览器是 ...

  2. 深入理解JS异步编程四(HTML5 Web Worker)

    >Web Workers 是 HTML5 提供的一个javascript多线程解决方案,我们可以将一些大计算量的代码交由web Worker运行而不冻结用户界面. 一:如何使用Worker We ...

  3. 深入理解JS异步编程(一)

    js事件概念 异步回调 首先了讲讲js中 两个方法 setTimeout()和 setInterval() 定义和用法: setTimeout() 方法用于在指定的毫秒数后调用函数或计算表达式. 语法 ...

  4. 深入理解JS异步编程二(分布式事件)

    PubSub模式 从原生的js角度,我们要监听某事件的方法就是利用addEventListener方法,但是当我们的页面趋于复杂,比如要向某个元素添加多个处理事件,那么就要用一个封装函数汇集多个处理函 ...

  5. 深入理解JS异步编程三(promise)

    jQuery 原本写一个小动画我们可能是这样的 $('.animateEle').animate({ opacity:'.5' }, 4000,function(){ $('.animateEle2' ...

  6. js异步编程

    前言 以一个煮饭的例子开始,例如有三件事,A是买菜.B是买肉.C是洗米,最终的结果是为了煮一餐饭.为了最后一餐饭,可以三件事一起做,也可以轮流做,也可能C需要最后做(等A.B做完),这三件事是相关的, ...

  7. 前端分享----JS异步编程+ES6箭头函数

    前端分享----JS异步编程+ES6箭头函数 ##概述Javascript语言的执行环境是"单线程"(single thread).所谓"单线程",就是指一次只 ...

  8. JS异步编程 (2) - Promise、Generator、async/await

    JS异步编程 (2) - Promise.Generator.async/await 上篇文章我们讲了下JS异步编程的相关知识,比如什么是异步,为什么要使用异步编程以及在浏览器中JS如何实现异步的.最 ...

  9. JS异步编程 (1)

    JS异步编程 (1) 1.1 什么叫异步 异步(async)是相对于同步(sync)而言的,很好理解. 同步就是一件事一件事的执行.只有前一个任务执行完毕,才能执行后一个任务.而异步比如: setTi ...

随机推荐

  1. 如何实现一个串行promise

    异步执行任务A.B.C,...... 1.使用数组的reduce方法,reduce里有四个参数,pre,next,index,arr, 2.如果then方法里返回的是一个promise对象,那么执行下 ...

  2. Carousel 走马灯

    在有限空间内,循环播放同一类型的图片.文字等内容 基础用法 适用广泛的基础用法 结合使用el-carousel和el-carousel-item标签就得到了一个走马灯.幻灯片的内容是任意的,需要放在e ...

  3. 01 numpy库(一)

    01-numpy NumPy(Numerical Python) 是 Python 语言的一个扩展程序库,支持大量的维度数组与矩阵运算,此外也针对数组运算提供大量的数学函数库. NumPy 是一个运行 ...

  4. kvm虚拟机操作相关命令及虚拟机和镜像密码修改

    虚拟机生命周期管理 1)查看kvm虚拟机状态 #virsh list --all 2)KVM虚拟机开机 # virsh start oeltest01 3)KVM虚拟机关机或断电 关机 默认情况下vi ...

  5. mysql sql常用语句

    1.说明:创建数据库 CREATE DATABASE database-name 2.说明:删除数据库 drop database dbname 3.说明:备份sql server --- 创建 备份 ...

  6. SqlServer Where后面Case When的使用实例

    SqlServer一个(用户表:a)中有两个字段都是用户ID 第一个ID是(收费员:id_remitter) 第二个ID是(退费员:id_returner) (收费表:b) 如何根据是否退费(F_RE ...

  7. ESP32开发之Windows开发环境

    电脑出了问题linux系统下的环境不知道怎么就挂了,在一次搭建,总是出错,没办法,只能在win10下一试. 1 下载交叉编译工具,最新版 找到并下载最新的工具链: https://dl.espress ...

  8. Block代码块中使用局部变量注意点

    第一次写代码遇到报这个错,实在是想不通为什么,按常理应该是不会有问题,报错的呀??纠结了一会之后只好仔细查看报错原因咯,原来是: 当我们在block代码块中使用局部变量时,就会很容易出现如图的错误. ...

  9. H3C 模拟器 pc与防火墙,交换机相连,在pc cmd下用telnet访问交换机和防火墙

    架构如图 实现目的 1 在pc端,用telnet访问核心交换机10.20.4.252 2 在pc端,用telnet访问二层交换机10.20.4.253 在此之前,pc_4,pc_5与交换机的配置不进行 ...

  10. BeanFactory 和FactoryBean的区别

    转自:https://www.cnblogs.com/aspirant/p/9082858.html BeanFacotry是spring中比较原始的Factory.如XMLBeanFactory就是 ...