一、回调函数(callback)

1、概念

A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.翻译:回调函数是作为参数传递给另一个函数的函数,在父函数实现后执行。

2、例子阐明

var fs = require("fs");var a function f(x) {    console.log(x)} function writeFile() {    fs.writeFile('input01.txt', 'www.baidu.com', function (err) {        if (!err) {            console.log("文件写入结束!")            a = 1        }    });} a = 0writeFile()f(a)输入后果:0文件写入结束!

代码阐明:设置一个全局变量 a = 0,后执行writeFile函数(也就是写入一个文件input01.txt),这个函数外面有一行c = 1,函数执行结束之后再跳进去调用f()函数,f()函数,就是把打印一个变量。

依照 “失常” 逻辑,首先 a=0,而后调用writeFile函数,该函数外面有一句a = 1,最初再调用f(a),又因为调用writeFile()是在f(a)之前,所以 a=1 这条语句必定是会被执行到,那么后果应该是打印1,后果居然是0,明明咱们在writeFile函数里咱们从新对 a 进行了赋值,为什么后果还是0呢?

因为程序运行到writeFile()这一行的时候,是一个比拟耗时的IO操作,JS碰到这种操作并不会停在原地始终期待直到函数执行结束,而是间接运行下一条代码(即f(a)),而此时 a = 1这一行代码其实并没有被执行到,所以打印进去的后果还是0 !

如果要打印出 a= 1,只须要将f(a)也放进writeFile()函数外面,就能保障a = 1之后再调用f(a):

var fs = require("fs");var a function f(x) {    console.log(x)} function writeFile() {     fs.writeFile('input01.txt', 'www.baidu.com', function (err) {        if (!err) {            console.log("文件写入结束!")            a = 1            f(a)        }    });} a = 0;writeFile();输入后果:文件写入结束!1

代码阐明: 先执行writeFile()函数,再执行writeFile中的f(a),a = 1 肯定会被执行到。

然而改成这样并不完满,因为这么做就相当于将f()"焊死"在writeFile()里了,如果此处我最终想调用的函数不是f()而是别的其余函数f1(),f2(),f3()...应该怎么写?难不成要写几个不同的writeFile(),而他们之间的区别仅仅是最初调用的那个函数不同?显然是不可获得,而应用回调函数就可解决:“关键字” callback 。(精确地说callback并不真的是Javascript里的关键字,然而鉴于大家都约定成俗把callback这个单词作为回调函数的默认抉择了,这里权且就不谨严地称它为"关键字"吧)

var fs = require("fs"); function f(x) {    console.log(x)} function writeFile(callback) { //callback,示意这个参数不是一个一般变量,而是一个函数    fs.writeFile('input01.txt', 'www.baidu.com', function (err) {        if (!err) {            console.log("文件写入结束!")            a = 1            callback(a) // 因为咱们传进来的函数名是f(),所以此行相当于调用一次f(c)        }    });}var a = 0writeFile(f) // 函数f作为一个参数传进writeFile函数输入后果:文件写入结束!1

代码阐明:呈现了两次callback,第一个callback呈现在 writeFile 的形参里,起定义的作用,示意这个参数并不是一个一般变量,而是一个函数,也就是后面所说的重点,即所谓的“以函数为参数”。 第二个callback呈现在a = 1上面,示意此处“执行”从形参传递进来的那个函数。

这样一来,writeFile()函数在执行结束之后到底调用哪个函数就变“活”了,如果咱们想writeFile()函数执行完之后并不是像第二个例子那样只能调用f(),而是还有别的函数比如说x() y() z(),那么只须要写成 writeFile(x),writeFile(y)... 就行了。

总结: 在大多数编程语言中,函数的形参总是从外向内传递参数,但在JS中,如果形参碰到“关键字” callback 则齐全相同,它示意从外向外反向调用某个内部函数。

此处并不一定非要写为“callback”,你能够任意写成abc, foo...等等。callback只是一种约定俗成的写法,它明确地通知代码阅读者:此处是一个回调函数。

有时候,咱们会看到一些函数的形参列表里间接嵌套一个函数的状况,其本质上依然是回调函数,因为没有了函数名,所以也称匿名函数。

var fs = require("fs"); function writeFile(callback) {     fs.writeFile('input01.txt', 'www.baidu.com', function (err) {        if (!err) {            console.log("文件写入结束!")            a = 1            callback(a)         }    });    }var a = 0writeFile(function (x) {    console.log(x)});输入后果:文件写入结束!1

writeFile()函数不变,只是在调用它的时候,间接将函数体嵌在参数列表里了,其作用跟上一个例子齐全一样。其实在本例中,fs.writeFile函数前面也有一个匿名回调函数 function (err) {},这个函数示意当文件写入结束后,就回调它,如果在写入过程中呈现了谬误,则通过变量err携带进去。

阐明: 在JS里,并非所有操作都是异步的,比方for循环,无论这个for循环须要耗时多长,零碎也肯定会等它转完之后才会执行上面的语句。

二、Promise介绍

1、回调天堂

首先咱们来看什么是回调天堂

在应用JavaScript时,为了实现某些逻辑常常会写出层层嵌套的回调函数,如果嵌套过多,会极大影响代码可读性和逻辑,这种状况也被成为回调天堂。
function pick(next){    setTimeout(()=>{        console.log('收到信息',new Date());        next();    },500);}function groundMouth(next){    setTimeout(()=>{        console.log('货物装载',new Date());        next();    },400);}function blow(next){    setTimeout(()=>{        console.log('打包出仓',new Date());        next();    },300);}function PEbag(next){    setTimeout(()=>{        console.log('集成装车',new Date());        next();    },200);}function pack(){    setTimeout(()=>{        console.log('发送运输',new Date());    },100);}pick(()=>{    groundMouth(()=>{        blow(()=>{            PEbag(()=>{                pack();            });        });    });});输入后果:收到信息 2022-03-20T14:24:15.363Z货物装载 2022-03-20T14:24:15.771Z打包出仓 2022-03-20T14:24:16.072Z集成装车 2022-03-20T14:24:16.273Z发送运输 2022-03-20T14:24:16.375Z

如果还有后续步骤,须要始终嵌套上来,这样代码的可读性就变差了。

如果应用Promise该怎么解决呢?

2、Promise介绍

2.1 Promise的状态

用 new Promise 实例化的promise对象有以下三个状态。

  • "has-resolution" 即Fulfilled resolve(胜利) 时。此时会调用 onFulfilled
  • "has-rejection" 即Rejected reject(失败) 时。此时会调用 onRejected
  • "unresolved" 即Pending 既不是resolve也不是reject的状态。也就是promise对象刚被创立后的初始化状态等

promise对象的状态,从Pending转换为Fulfilled或Rejected之后, 这个promise对象的状态就不会再产生任何变动。也就是说,只有异步操作的后果能够决定以后是哪一种状态,其余任何操作都无奈扭转这种状态;一旦状态扭转,就不会再扭转。

Promise与Event等不同,在 .then 后执行的函数能够必定地说只会被调用一次。

Promise中的函数是同步的,只有.then后的函数才是异步的

2.2 结构器

创立一个Promise对象,能够应用new调用Promise的结构器来进行实例化。

var promise = new Promise(function(resolve, reject) { // 异步解决// 解决完结后、调用resolve 或 reject});

2.3 实例办法

  • Promise.prototype.then

对通过new生成的promise对象为了设置其值在 resolve (胜利)/ reject(失败)时调用的回调函数 能够应用 promise.then()实例办法(也就是说作用是为 Promise 实例增加状态扭转时的回调函数。)。

promise.then(onFulfilled, onRejected)

then办法的第一个参数是 Resolved 状态的回调函数, 第二个参数( 可选) 是 Rejected 状态的回调函数。

  • resolve(胜利)时 onFulfilled 会被调用
  • reject(失败)时 onRejected 会被调用
onFulfilled 、 onRejected 两个都为可选参数。

then办法返回的是一个新的 Promise 实例( 留神,不是原来那个 Promise 实例)。 因而能够采纳链式写法, 即then办法前面再调用另一个`then办法。

getJSON("/post/1.json") //返回一个Promise对象.then(function(post) {      return getJSON(post.commentURL);  //返回一个Promise对象}).then(function funcA(comments) {      console.log("Resolved: ", comments);  }, function funcB(err) {      console.log("Rejected: ", err);  });

下面的代码应用then办法,顺次指定了两个回调函数。 第一个回调函数实现当前,会将返回后果作为参数,传入第二个回调函数。采纳链式的then,能够指定一组依照秩序调用的回调函数。

  • Promise.prototype.catch()

promise.then胜利和失败时都能够应用。另外在只想对异样进行解决时能够采纳Promise.then(undefined, onRejected)这种形式,只指定reject时的回调函数即可。

Promise.prototype.catch办法是.then(null, rejection) 的别名, 用于指定产生谬误时的回调函数,等同于抛出谬误。
上文的代码能够革新成如下:

getJSON("/post/1.json") //返回一个Promise对象.then(function(post) {      return getJSON(post.commentURL);  //返回一个Promise对象}).then(function (comments) {      console.log("Resolved: ", comments);  }).catch(err) {      console.log("Rejected: ", err);  });  

须要留神的是,如果 Promise 状态曾经变成Resolved, 再抛出谬误是有效的。

var promise = new Promise(function(resolve, reject) {    resolve('success');    throw new Error('error');  });  promise    .then(function(value) {        console.log(value)    })    .catch(function(error) {        console.log(error)    });输入后果:success

下面代码中, Promise 在resolve语句前面,再抛出谬误,不会被捕捉, 等于没有抛出。

Promise 对象的谬误具备“ 冒泡” 性质, 会始终向后传递, 直到被捕捉为止。 也就是说, 谬误总是会被下一个catch语句捕捉。

var catchError = new Promise(function(resolve, reject) {    setTimeout(function(){      resolve('success')  }, 1000)  }) catchError.then(function(value){  console.log('a')}).then(function(value){  throw new Error('test');    console.log('b')}).then(function(value){  console.log('c')}).catch(function(error){  console.log(error)})输入后果:aError: test    at D:\upyun\test\promise\promise12.js:26:9

下面代码中,一共有四个Promise 对象:一个由'catchError'产生, 三个由then产生。它们之中的第二个then办法出了谬误,中断了上面的then办法,间接被最初一个catch捕捉。

倡议总是应用catch办法, 而不应用then办法的第二个处理错误的参数。

跟传统的try / catch代码块不同的是,如果没有应用catch办法指定错误处理的回调函数,Promise 对象抛出的谬误不会传递到外层代码, 即不会有任何反馈。

var foo = function() {    return new Promise(function(resolve, reject) {        //  上面一行会报错,因为 a 没有申明        resolve(a + 2);    });  };  foo().then(function() {    console.log('everything is great');  });输入后果:ReferenceError: a is not defined

下面代码中,foo函数产生的 Promise 对象会报错, 然而因为没有指定catch办法,这个谬误不会被捕捉,也不会传递到外层代码, 导致运行后没有任何输入。

留神, Chrome 浏览器不恪守这条规定, 它会抛出谬误“ ReferenceError: x is not defined”。
var promise = new Promise(function(resolve, reject) {    resolve("success");    setTimeout(function() {        throw new Error('error')    }, 0)  });  promise.then(function(value) {    console.log(value)  }); 输入后果:successD:\upyun\test\promise\promise12.js:49      throw new Error('error')      ^Error: error

下面代码中,Promise指定在下一轮“ 事件循环” 再抛出谬误, 后果因为没有指定应用try...catch语句,就冒泡到最外层,成了未捕捉的谬误。 因为此时,Promise 的函数体曾经运行完结了, 所以这个谬误是在Promise函数体外抛出的。

Node.js 有一个unhandledRejection事件,专门监听未捕捉的reject谬误。unhandledRejection事件的监听函数有两个参数, 第一个是谬误对象, 第二个是报错的 Promise 实例, 它能够用来理解产生谬误的环境信息。

process.on('unhandledRejection', function(err, p) {      console.error(err.stack)  }); 
须要留神的是,catch办法返回的还是一个Promise对象,因而前面还能够接着调用then办法。
var foo = function() {    return new Promise(function(resolve, reject) {        //  上面一行会报错,因为 a 没有申明        resolve(a + 2);    });  };  foo()    .catch(function(error) {        console.log('Error', error);    })    .then(function() {        console.log('success');    });输入后果:Error ReferenceError: a is not definedsuccess

下面代码运行完catch办法指定的回调函数,会接着运行前面那个then办法指定的回调函数。 如果没有报错, 则会跳过catch办法。

2.4 静态方法

Promise 这样的全局对象还领有一些静态方法。

包含 Promise.all() 还有 Promise.resolve() 等在内,次要都是一些对Promise进行操作的 辅助办法。

  • Promise.all

Promise.all() 办法接管一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输出,并且只返回一个Promise实例, 那个输出的所有promise的resolve回调的后果是一个数组。这个Promise的resolve回调执行是在所有输出的promise的resolve回调都完结,或者输出的iterable里没有promise了的时候。它的reject回调执行是,只有任何一个输出的promise的reject回调执行或者输出不非法的promise就会立刻抛出谬误,并且reject的是第一个抛出的错误信息。

const promise1 = Promise.resolve(3);const promise2 = 42;const promise3 = new Promise((resolve, reject) => {  setTimeout(resolve, 100, 'foo');});Promise.all([promise1, promise2, promise3]).then((values) => {  console.log(values);});输入后果:[ 3, 42, 'foo' ]
语法
Promise.all(iterable);
参数
  • iterable
  • 一个可迭代对象,如 Array 或 String
返回值
  • 如果传入的参数是一个空的可迭代对象,则返回一个已实现(already resolved) 状态的 Promise
  • 如果传入的参数不蕴含任何 promise,则返回一个异步实现(asynchronously resolved)  Promise。留神:Google Chrome 58 在这种状况下返回一个已实现(already resolved) 状态的 Promise
  • 其它状况下返回一个解决中(pending)Promise。这个返回的 promise 之后会在所有的 promise 都实现或有一个 promise 失败时异步地变为实现或失败。返回值将会依照参数内的 promise 顺序排列,而不是由调用 promise 的实现程序决定。
阐明

此办法在汇合多个 promise 的返回后果时很有用。

实现(Fulfillment): 如果传入的可迭代对象为空,Promise.all 会同步地返回一个已实现(resolved)状态的promise
如果所有传入的 promise 都变为实现状态,或者传入的可迭代对象内没有 promisePromise.all 返回的 promise 异步地变为实现。

在任何状况下,Promise.all 返回的 promise 的实现状态的后果都是一个数组,它蕴含所有的传入迭代参数对象的值(也包含非 promise 值)。

失败/回绝(Rejection): 如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个后果给失败状态的回调函数,而不论其它 promise 是否实现。

-Promise.resolve()

Promise.resolve(value) 办法返回一个以给定值解析后的Promise 对象。如果这个值是一个 promise ,那么将返回这个 promise ;如果这个值是thenable(即带有"then" 办法),返回的promise会“追随”这个thenable的对象,采纳它的最终状态;否则返回的promise将以此值实现。此函数将类promise对象的多层嵌套展平。

语法
Promise.resolve(value);
参数

value

将被Promise对象解析的参数,也能够是一个Promise对象,或者是一个thenable。

返回值

返回一个带着给定值解析过的Promise对象,如果参数自身就是一个Promise对象,则间接返回这个Promise对象。

形容

静态方法 Promise.resolve返回一个解析过的Promise对象。

1. 应用动态`Promise.resolve`办法Promise.resolve("Success").then(function(value) {  console.log(value); }, function(value) {  // 不会被调用});输入后果:Success2.resolve一个数组var p = Promise.resolve([1,2,3]);p.then(function(v) {  console.log(v[0]); // 1});

2.5 还有须要留神,Promise创立后回立即执行

var promise = new Promise(function(resolve, reject) {    console.log('Promise');    resolve();  });  promise.then(function() {    console.log('Resolved.');  });  console.log('hello world'); 输入后果:Promisehello worldResolved.

下面代码中, Promise 新建后立刻执行, 所以首先输入的是“ Promise”。 而后, then办法指定的回调函数, 将在以后脚本所有同步工作执行完才会执行, 所以“ Resolved” 最初输入。

2.6 Promise也是有毛病的

  • 无奈勾销Promise,一旦新建它就会立刻执行,无奈中途勾销。
  • 如果不设置回调函数, Promise外部抛出的谬误,不会反馈到内部。
  • 当处于Pending状态时, 无奈得悉目前停顿到哪一个阶段(刚刚开始还是行将实现)。

2.7 创立Promise对象流程

  • new Promise(fn)返回一个Promise对象
  • fn中指定异步等解决逻辑

    • 处理结果失常的话,调用 resolve(处理结果值)
    • 处理结果谬误的话,调用 reject(Error对象)

2.8 创立XHR的promise对象

首先,创立一个用Promise把XHR解决包装起来的名为 getURL 的函数。

function getURL(URL) {  return new Promise(function (resolve, reject) {      var req = new XMLHttpRequest();       req.open('GET', URL, true);       req.onload = function () {          if (req.status === 200) {               resolve(req.responseText);          } else {              reject(new Error(req.statusText));          }       };            req.onerror = function () {           reject(new Error(req.statusText));      };      req.send();   });}// 运行示例var URL = "http://httpbin.org/get"; getURL(URL).then(function onFulfilled(value){  console.log(value); }).catch(function onRejected(error){  console.error(error); });

getURL 只有在通过XHR获得后果状态为200时才会调用 resolve,而其余状况(获得失败)时则会调用 reject 办法。

resolve(req.responseText)

resolve函数的作用是, 将 Promise对象的状态从“ 未实现” 变为“ 胜利”( 即从 Pending变为Resolved),在异步操作胜利时调用,并将异步操作后果,作为参数传递进来。

参数并没有特地的规定,基本上把要传给回调函数参数放进去就能够了。 ( then 办法能够接管到这个参数值)

reject(new Error(req.statusText))

reject函数的作用是,将 Promise 对象的状态从“ 未实现” 变为“ 失败”( 即从 Pending 变为Rejected),在异步操作失败时调用,并将异步操作报出的谬误,作为参数传递进来。

上文中,XHR中 onerror事件被触发的时候就是产生谬误时,所以天经地义调用 reject 。产生谬误时,创立一个Error对象后再将具体的值传进去。传给 的参数也没有什么非凡的限度,个别只有是Error对象(或者 继承自Error对象)就能够。

2.9 解决下面的回调天堂

function pick(){  return new Promise((resolve, reject) => {    setTimeout(()=>{      console.log('收到信息',new Date());      resolve();  },500);  })}function groundMouth(){  return new Promise((resolve, reject) => {    setTimeout(()=>{      console.log('货物装载',new Date());      resolve();  },400);  })}function blow(){  return new Promise((resolve, reject) => {    setTimeout(()=>{      console.log('打包出仓',new Date());      resolve();  },300);  })}function PEbag(){  return new Promise((resolve, reject) => {    setTimeout(()=>{      console.log('集成装车',new Date());      resolve();  },200);  })}function pack(){  return new Promise((resolve, reject) => {    setTimeout(()=>{      console.log('发送运输',new Date());      resolve();  },100);  })}pick()  .then(groundMouth)  .then(blow)  .then(PEbag)  .then(pack)// pick(()=>{//   groundMouth(()=>{//       blow(()=>{//           PEbag(()=>{//               pack();//           });//       });//   });// });输入后果:收到信息 2022-03-21T03:24:56.520Z货物装载 2022-03-21T03:24:56.925Z打包出仓 2022-03-21T03:24:57.235Z集成装车 2022-03-21T03:24:57.436Z发送运输 2022-03-21T03:24:57.544Z

三、async await

首先阐明一下异步,以及常见的微工作和宏工作

1、异步,微工作,宏工作

异步
  • 异步是用来解决JS单线程期待这种问题的
  • 异步是基于回调函数的模式来实现的
  • 常见的异步有:setTimeout、ajax、Promise……then、async/await、图片加载、网络申请资源
  • 牢记5个版块 Call Stack、 Web APIs、 Browser console、 Callback Queue 、 micro task queue 这五个版块走漏出异步的执行过程
  • 宏工作是在DOM渲染后触发,微工作是在DOM渲染前触发

    常见微工作和宏工作
  • 常见的微工作: Promise……then、 async/await
  • 常见的宏工作: setTimeout、setInterval

2、async await介绍

async/await 是ECMAScript 2017提出的内容。但事实上它们只是Promise的语法糖。

因为Promise的呈现次要是为了解决异步的回调天堂问题。将噩梦般的嵌套回调变为了优雅的管道式回调。但这始终是逃不掉“回调”二字。而async/await虽说只是Promise的语法糖,但让你“脱离”了回调,拥抱了同步代码

1.执行async函数,返回的是Promise对象2.await必须在async包裹之下执行3.await相当于Promise的then并且同一作用域下await上面的内容全副作为then中回调的内容4.try……catch可捕捉异样,代替了Promise的catch5.异步中先执行微工作,再执行宏工作
async function fn() {  return '我是async函数';}console.log('async:', fn());输入后果:async: Promise { '我是async函数' }

这里的fn() 相当于 Promise.resolve('我是async函数')

(async function() {  const p = Promise.resolve('success');  const data = await p; // await就相当于Promise.then, 故data就是then的参数  console.log(data);    // 这里的代码为then中回调的内容})();输入后果:success

下面的这段代码,如果把async删掉,后果肯定会报错!

(async function() {  const p = Promise.reject('err');  // await + try...catch 相当于 Promise.catch  try {    const res = await p;    console.log(res);  } catch(ex) {  // ex 来源于reject()外面的数    console.error(ex);   }})();输入后果:err

下面的代码中:await + try...catch 相当于 Promise.catch

3、同步,异步,微工作,宏工作的执行程序

先同后异,先微后宏
  • 例子如下:
async function async1 () {  console.log('async1 start');      // 2  await async2();  console.log('async1 end');        // 6}async function async2 () {  console.log('async2');            // 3}console.log('script start');        // 1setTimeout(function () {  console.log('setTimeout');        // 8}, 0)async1()new Promise (function (resolve) {  console.log('promise1')           // 4  resolve()}).then(function () {  console.log('promise2')           // 7})console.log('script end')           // 5输入后果:script startasync1 startasync2promise1script endasync1 endpromise2setTimeout
  • 代码剖析:
1. 先执行同步代码。2. 所以首先执行 console.log('script start');3. setTimeout为宏工作,先不执行 4. 执行async1函数 console.log('async1 start'); 以及 async2(); await因为是Promise.then的语法糖是异步代码,先不执行 5. new Promise() 外部代码要执行,前面的then的内容为微工作先不执行6.执行console.log('script end')7.同步代码执行完结 8.开始按代码程序执行微工作9.先执行 console.log('async1 end'); 后面说过,await上面的代码相当于then里回调的内容10.new Promise.then外面的内容 console.log('promise2')11. 最初执行 宏工作代码,即setTimeout里的内容。

四、promisify

1、 Callback 与 Promise 间的桥梁 —— promisify

  • Promisify
1.npm 装置npm install bluebird2. Node.js后端引入(Common.js标准)var Promise = require("bluebird");3.或者在 ES6 中import * as Promise from "bluebird";4.如果那个 ES6 导入[不起作用](https://github.com/petkaantonov/bluebird/pull/1594)import {Promise} from "bluebird";

Promise 很好地解决了异步办法的回调天堂、提供了咱们在异步办法中应用 return 的能力,并将 callback 的调用纳入了本人的治理,而不是交给异步函数后咱们就无能为力了(常常有 callback 被莫名调用两次而导致程序出错)。

2、promisify介绍

就是“promise 化”,将一个不是promise的办法变成 promise 。

  • 作用

将本来须要通过传入回调参数来实现回调执行(或者叫同步执行)改为利用promise.then的形式来调用,从而实现逻辑上的同步操作

const fs = require('fs');const Promise = require('bluebird');// 原有的callback调用fs.readFile('input02.txt', function(err, data) {  if (!err) {      console.log('===: ' + data.toString());  } else {      console.log(err);  }});// promisify后var readFileAsync = Promise.promisify(fs.readFile);readFileAsync('input01.txt').then(data => {    console.log('---: ' + data.toString());}, err => {    console.log(err);});输入后果:===: www.baidu.com---: 我是通过fs.writeFile 写入文件的内容

这两个办法成果上是等价的。后者掌控性更好!

那么什么样的办法能够通过 promisify 变成 promise 呢?这里就须要介绍一个名词,nodeCallback。什么样的 callback 叫 nodeCallback ?

  • nodeCallback

    有两个条件:1. 回调函数在主函数中的参数地位必须是最初一个;2. 回调函数参数中的第一个参数必须是 error 。

回调函数在主函数中的参数地位

// 正确function main(a, b, c, callback) {  } // 谬误function main(callback, a, b, c) {   }

回调函数参数中的第一个参数必须是 error

// 正确function callback(error, result1, result2) {  } // 谬误function callback(result1, result2, error) {   }

通过 nodeCallback ,咱们定义了一个能被 promisify 的函数的格局,即,满足 nodeCallback 模式的办法,咱们能够通过 promisify 来让它变成一个返回 promise 的办法。

3、promisify应用

const Promise = require('bluebird');async function main(a, b, callback) {  const array1 = a.concat(b);  console.log('a=' + a);  console.log('b=' + b);  return callback(null, array1);}async function foo(c, d, callback) {  const array2 = c.concat(d);  console.log('c=' + c);  console.log('d=' + d);  callback(null, array2);}async function fu(a,b) {  // Promise.promisify 将不是 promise 的办法转为 promise 办法(实现同步), 如果有 .then() 则为异步。  const function1 = Promise.promisify(main);  const function2 = Promise.promisify(foo);  const data1 = await function1(a,b);   // await就相当于Promise.then, 故data就是then的参数  console.log('data1:' + data1);        // 这里的代码为then中回调的内容  for(let i = 0; i < data1.length; i++) {    const data = data1[i];    const data2 = await function2(data1, b);    console.log('data2:' + data2);  }}const arr1 = [2,3,4,5];const arr2 = [45,6,7,8];fu(arr1,arr2);输入:a=2,3,4,5b=45,6,7,8data1:2,3,4,5,45,6,7,8c=2,3,4,5,45,6,7,8d=45,6,7,8data2:2,3,4,5,45,6,7,8,45,6,7,8c=2,3,4,5,45,6,7,8d=45,6,7,8data2:2,3,4,5,45,6,7,8,45,6,7,8c=2,3,4,5,45,6,7,8d=45,6,7,8data2:2,3,4,5,45,6,7,8,45,6,7,8c=2,3,4,5,45,6,7,8d=45,6,7,8data2:2,3,4,5,45,6,7,8,45,6,7,8c=2,3,4,5,45,6,7,8d=45,6,7,8data2:2,3,4,5,45,6,7,8,45,6,7,8c=2,3,4,5,45,6,7,8d=45,6,7,8data2:2,3,4,5,45,6,7,8,45,6,7,8c=2,3,4,5,45,6,7,8d=45,6,7,8data2:2,3,4,5,45,6,7,8,45,6,7,8c=2,3,4,5,45,6,7,8d=45,6,7,8data2:2,3,4,5,45,6,7,8,45,6,7,8

代码阐明:fu()函数中,将main()函数和foo()函数进行了promisify化。将asycn await函数转换为 promise 函数。其中callback中,须要给第一个参数给为:null。如果不为null会有如下谬误:

const Promise = require('bluebird');async function main(a, b, callback) {  const array1 = a.concat(b);  console.log('a=' + a);  console.log('b=' + b);  return callback(array1); }async function foo(c, d, callback) {  const array2 = c.concat(d);  console.log('c=' + c);  console.log('d=' + d);  callback(null, array2);}async function fu(a,b) {  // Promise.promisify 将不是 promise 的办法转为 promise 办法(实现同步), 如果有 .then() 则为异步。  const function1 = Promise.promisify(main);  const function2 = Promise.promisify(foo);  const data1 = await function1(a,b);   // await就相当于Promise.then, 故data就是then的参数  console.log('data1:' + data1);        // 这里的代码为then中回调的内容  for(let i = 0; i < data1.length; i++) {    const data = data1[i];    const data2 = await function2(data1, b);    console.log('data2:' + data2);  }}const arr1 = [2,3,4,5];const arr2 = [45,6,7,8];fu(arr1,arr2);

错误信息:

a=2,3,4,5b=45,6,7,8node:internal/process/promises:265            triggerUncaughtException(err, true /* fromPromise */);            ^[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "[object Array]".] {  code: 'ERR_UNHANDLED_REJECTION'}