搜索
首页web前端js教程javascript中async的用法详解

javascript中async的用法详解

Aug 23, 2017 pm 02:00 PM
asyncjavascriptjs

本篇文章主要介绍了理解javascript async的用法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

写在前面

本文将要实现一个顺序读取文件的最优方法,实现方式从最古老的回调方式到目前的async,也会与大家分享下本人对于thunk库与co库的理解。实现的效果:顺序读取出a.txt与b.txt,将读出的内容拼接成为一个字符串。

同步读取


const readTwoFile = () => {
  const f1 = fs.readFileSync('./a.txt'),
    f2 = fs.readFileSync('./b.txt');
  return Buffer.concat([f1, f2]).toString();
};

这种方式最利于我们理解,代码也很清楚,没有过多的嵌套,很好的维护,但是这种有着最大的问题,那就是性能,node所倡导的就是异步i/o来处理密集i/o,而同步的读取,很大的程度上浪费着服务器的cpu,这种方式的弊端明显的大于好处,所以直接pass掉。(其实node的任何异步编程的解决方案的目标都是要达到同步的语义,异步的执行。)

利用回调读取


const readTwoFile = () => {
  let str = null;
  fs.readFile('./a.txt', (err, data) => {
    if (err) throw new Error(err);
    str = data;
    fs.readFile('./b.txt', (err, data) => {
      if (err) throw new Error(err);
      str = Buffer.concat([str, data]).toString();
    });
  });
};

利用回调的方式,实现起来很简单,直接的嵌套下去就好,但是这种情况下很容易造成的就是不易维护,难以读懂的情况,最为极致的情况的就是回调地狱。

Promise实现


const readFile = file => 
  new Promise((reslove, reject) => {
    fs.readFile(file, (err, data) => {
      if (err) reject(err);
      reslove(data);
    });
  });
const readTwoFile = () => {
  let bf = null;
  readFile('./a.txt')
    .then(
      data => {
        bf = data;
        return readFile('./b.txt');
      }, 
      err => { throw new Error(err) }
    )
    .then(
      data => {
        console.log(Buffer.concat([bf, data]).toString())
      }, 
      err => { throw new Error(err) }
    );
};

Promise可以将横向增长的回调转化为纵向增长,能解决一些问题,但是Promise造成的问题就是代码冗余,一眼看过去,全部是then,也不是很爽,但是相比于回调函数嵌套来说,已经有了很大的提升。

yield

Generator很多语言中都有,本质上是协程,下面就来看一下协程,线程,进程的区别与联系:

  • 进程:操作系统中分配资源的基本单位

  • 线程:操作系统中调度资源的基本单位

  • 协程:比线程更小的的执行单元,自带cpu上下文,一个协程一个栈

一个进程中可能存在多个线程,一个线程中可能存在多个协程,进程、线程的切换由操作系统控制,而协程的切换由程序员自身控制。异步i/o利用回调的方式来应对i/o密集,同样的使用协程也可以来应对,协程的切换并没有很大的资源浪费,将一个i/o操作写成一个协程,这样进行i/o时可以吧cpu让给其他协程。

js同样支持协程,那就是yield。使用yield给我们直观的感受就是,执行到了这个地方停了下来,其他的代码继续跑,到你想让他继续执行了,他就是会继续执行。


function *readTwoFile() {
  const f1 = yield readFile('./a.txt');
  const f2 = yield readFile('./b.txt'); 
  return Buffer.concat([f1, f2]).toString();
}

yield下的顺序读取呈现的也是一种顺序读取的方式,对于readFile来看有两种不同的实现方式,

利用thunkify


const thunkify = (fn, ctx) => (...items) => (done) => {
  ctx = ctx || null;
  let called = false;
  items.push((...args) => {
    if (called) return void 0;
    called = true;
    done.apply(ctx, args);
  });
  try {
    fn.apply(ctx, items);  
  } catch(err) {
    done(err);
  }
};

thunkify函数就是一种柯里化得思想,最后的传入参数done就为回调函数,利用thunkify可以很轻松的实现yield函数的自动化流程:


const run = fn => {
  const gen = fn();
  let res;
  (function next(err, data) {
    let g = gen.next(data);
    if (g.done) return void 0;
    g.value(next);
  })();
};

利用Promise


const readFile = file => 
  new Promise((reslove, reject) => {
    fs.readFile(file, (err, data) => {
      if (err) reject(err);
      reslove(data);
    });
  });
const run = fn => {
  const gen = fn();
  let str = null;
  (function next(err, data) {
    let res = gen.next(data);
    if (res.done) return void 0;
    res.value.then(
      data => {
        next(null, data);
      }, 
      err => { throw new Error(err); }
    );
  })();
};
run(readTwoFile);

上面两种方式都可以达到自动执行yield的过程,那么有没有一种方式,可以兼容这两种实现方式呢,tj大神又给出了一个库,那就是co库,先来看下用法:


// readTwoFile的实现与上面类似,readFile既可以利用Promise也可以利用thunkify
// co库返回一个Promise对象
co(readTwoFile).then(data => console.log(data));

来看下co库的实现,co库默认会返回一个Promise对象,对于yield之后的值(如上面的res.value),co库会将其转换为一个Promise。实现思想很简单,基本还是利用递归的方式,大体的思路如下:


const baseHandle = handle => res => {
  let ret;
  try {
    ret = gen[handle](res);
  } catch(e) {
    reject(e);
  }
  next(ret);
};
function co(gen) {
  const ctx = this,
    args = Array.prototype.slice.call(arguments, 1);
  return new Promise((reslove, reject) => {
    if (typeof gen === 'function') gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== 'function') return resolve(gen);

    const onFulfilled = baseHandle('next'),
      onRejected = baseHandle('throw');

    onFulfilled();

    function next(ret) {
      if (ret.done) reslove(ret.value);
      // 将yield的返回值转换为Proimse
      const value = toPromise.call(ctx, ret.value);
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      return onRejected(new TypeError('yield type error'));
    }
  });
}

toPromise就是将一些类型转换为Promise,从这里我们可以看出的是可以将哪些类型放在yield后面,这里就来看一个常用的:


// 把thunkify之后的函数转化为Promise的形式
function thunkToPromise(fn) {
  const ctx = this;
  return new Promise(function (resolve, reject) {
    fn.call(ctx, function (err, res) {
      if (err) return reject(err);
      if (arguments.length > 2) res = slice.call(arguments, 1);
      resolve(res);
    });
  });
}

最近Node已经支持了async/await,可以利用其来做异步操作:

终极解决


const readFile = file => 
  new Promise((reslove, reject) => {
    fs.readFile(file, (err, data) => {
      if (err) reject(err);
      reslove(data);
    });
  });
const readTwoFile = async function() {
  const f1 = await readFile('./a.txt');
  const f2 = await readFile('./b.txt');  
  return Buffer.concat([f1, f2]).toString();
};
readTwoFile().then(data => {
  console.log(data);
});

async/await做的就是将Promise对象给串联起来,避免了then的调用方式,代码非常的易读,就是一种同步的方式。不再需要借助其他外界类库(比如co库)就可以优雅的解决回调的问题

以上是javascript中async的用法详解的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
JavaScript的角色:使网络交互和动态JavaScript的角色:使网络交互和动态Apr 24, 2025 am 12:12 AM

JavaScript是现代网站的核心,因为它增强了网页的交互性和动态性。1)它允许在不刷新页面的情况下改变内容,2)通过DOMAPI操作网页,3)支持复杂的交互效果如动画和拖放,4)优化性能和最佳实践提高用户体验。

C和JavaScript:连接解释C和JavaScript:连接解释Apr 23, 2025 am 12:07 AM

C 和JavaScript通过WebAssembly实现互操作性。1)C 代码编译成WebAssembly模块,引入到JavaScript环境中,增强计算能力。2)在游戏开发中,C 处理物理引擎和图形渲染,JavaScript负责游戏逻辑和用户界面。

从网站到应用程序:JavaScript的不同应用从网站到应用程序:JavaScript的不同应用Apr 22, 2025 am 12:02 AM

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。

Python vs. JavaScript:比较用例和应用程序Python vs. JavaScript:比较用例和应用程序Apr 21, 2025 am 12:01 AM

Python更适合数据科学和自动化,JavaScript更适合前端和全栈开发。1.Python在数据科学和机器学习中表现出色,使用NumPy、Pandas等库进行数据处理和建模。2.Python在自动化和脚本编写方面简洁高效。3.JavaScript在前端开发中不可或缺,用于构建动态网页和单页面应用。4.JavaScript通过Node.js在后端开发中发挥作用,支持全栈开发。

C/C在JavaScript口译员和编译器中的作用C/C在JavaScript口译员和编译器中的作用Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

JavaScript在行动中:现实世界中的示例和项目JavaScript在行动中:现实世界中的示例和项目Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

了解JavaScript引擎:实施详细信息了解JavaScript引擎:实施详细信息Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具

mPDF

mPDF

mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),